How I deploy the Plant-Waterify API

Here is how I deploy the Plant-Waterify API to my VPS

This is a very basic deployment process I use to automatically push updates made to master to production for the API for my Plant Waterify project.

NOTE: You will need to have a Gitlab runner already set up for deployments to actually run on. I may do an article on Gitlab Runners in the future.


What are we deploying

Here is the repo for the API:

More details can be found in my other post linked above, but this is an API built in Go running on a VPS (that also hosts the site you’re reading this on, as well as all my other projects).



I have a Docker image I created that I use for Hugo deployments. Here is the DockerHub link:

If you want to create your own version, you can find a starter Dockerfile here.


Gitlab CI/CD

The gitlab-ci.yml does the following steps:

  • Specify the Docker image
  • Configure the stages (just one in this case)
  • Specify what branches this deployment runs for (master only)
  • Set up SSH keys and known_hosts to authenticate and deploy to the VPS
  • Run a make command to do the build and actual deployment


Gitlab Setup

First, you will need to create the variables in the project settings in Gitlab. Navigate to the project, Settings -> CI/CD


Then you will need to add a Gitlab variable for each of the following items we will use later (I have additional for other projects):

  • SSH private key (SSH key authentication to your server - likely the contents of your ~/.ssh/id_rsa file)
  • SSH known hosts (to prevent the SSH connection from asking if you want to accept the connection during the script)

I was able to get the proper known_hosts file contents using this command:

These variables can be called in the gitlab-ci.yml file.



Gitlab CI YAML

Here is the actual .gitlab-ci.yml file:


image: jasonsdocker2018/hugoserver:latest

  - deploy

  stage: deploy
  image: jasonsdocker2018/hugoserver:latest
    - master
    ## Install ssh-agent if not already installed, it is required by Docker.
    ## (change apt-get to yum if you use an RPM-based image)
    - 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'

    ## Run ssh-agent (inside the build environment)
    - eval $(ssh-agent -s)

    ## Create the SSH directory and give it the right permissions
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh

    ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
    ## We're using tr to fix line endings which makes ed25519 keys work
    ## without extra base64 encoding.
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - echo "$SSH_PRIVATE_KEY"
    - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts

    ## Optionally, if you will be using any Git commands, set the user name and
    ## and email.
    # - git config --global ""
    # - git config --global "User name"

    - make deploy



The last step is the make deploy. I use a makefile to build and deploy - this can be used on a dev machine or, with the above process, via Gitlab.

Here is the makefile:

PROJECT_NAME := "plant-waterify-api"
PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/)
GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)
REV := $(shell git rev-parse HEAD)
TOKEN := $(shell cat config.yaml | grep RollbarToken | awk '{ print $$2 }')
USER := $(shell whoami)
BRANCH := $(shell git branch | awk '{print $$2}')
SERVER := ""

dep: ## Get the dependencies
	@go get -v -d ./...

build: dep ## Build the binary file
	@go build -v $(PKG)

build-mac: dep ## Build the binary file for MacOS
	@env GOOS=darwin go build -v $(PKG)

deploy: build
	ssh $(SERVER) "sudo service plant-waterify-api stop"
	rsync plant-waterify-api $(SERVER):/home/jason/www-data/plant-waterify-api/
	rsync $(SERVER):/home/jason/www-data/plant-waterify-api/
	ssh $(SERVER) "sudo service plant-waterify-api start"
	curl -H "Content-Type: application/json" -H "X-Rollbar-Access-Token: $(TOKEN)" -X POST -d '{"environment":"production","revision":"$(REV)", "local_username":"$(USER)","comment":"Branch: $(BRANCH)"}'


What this does:

  • ssh to the VPS and stop the current running service
  • rsync the built file
  • rsync the script used to actually start the go app (this is what the .service file actually calls)
  • start the service again
  • if we get here, deployment was successful, let Rollbar know via a curl command


The service can be set up using this.

That’s it! Deployment successful!