May 12, 2024
Deploying Go on a VPS with Github actions
4 min read
Ever since DHH migrated from the cloud to their own machines last year, the indie community have been trying to replicate that. Add in the Serverless drama over the last few weeks and more people have set up Linode and others to run their workloads instead.
Most of these cloud platforms have an easy way to hook into your Github repo to auto deploy your app on changes/push, the same cannot be said for a traditional machine.
Earlier on, I used to run a K8s cluster for a bunch of other things so I just naturally just ended up hosting small projects I work on like Fotion there. But over the last few days, I had to kill the K8s cluster as it was no longer needed, thus making running k8s for Fotion and others extremely overkill, so I migrated them all to Linode.
This article is a simple walkthrough of how deployment is automated amongst others.
Check out Fotion at https://usefotion.app
What I use
- Linode (bare machine)
- Infisical Cloud ( to store secrets ). No AWS secrets and Hashicorp Vault too complicated to selfhost.
- Github actions
- Terraform and Ansible
I will use fotion
in this example but you can always change to your
preferred app names.
This guide assumes you already have the machine created and running
Create a SystemD file on the server
I use Ansible to do this but for simplicity case, we will do it the crude way
The first thing to do is to create a SystemD service. It allows you
configure the entire lifecycle of an application using the systemctl
command.
You need to ssh into the server :)
You will need to create a file in the location /usr/lib/systemd/system/fotion.service
[Unit]
Description=Fotion Backend API
[Service]
ExecStart=/usr/local/bin/fotion --env.file /root/fotion/.env
User=root
Group=root
UMask=007
[Install]
WantedBy=multi-user.targe
Take a look at the ExecStart
section, we start a binary called fotion
. You
can verify this works by running systemctl start fotion
. This should succeed
but you can always verify the status of a systemd service by running
journalctl -xeu fotion
. This should show us that the service failed -
expected because we do not have the binary
Automate deployment using Github Actions
The next piece is to automate the deployment. This deployment step will add the
fotion
binary into the server. And replace the binary in place, then restart the
server.
Another important piece of information is certain configuration values are loaded
via environment variables. I do not anually edit the .env
file, instead I use
Infisical to manage them. Our Github actions
deployment script will fetch the latest updates from Infisical and write them to
a .env
file which will then be copied to the server using scp
.
name: Deploy to Linode
on:
push:
branches:
- main
jobs:
deploy-fotion:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# - name: Migrate postgres
# run: goose -dir migrations postgres "host=${{ secrets.POSTGRES_HOST }} port=5432 user=${{ secrets.POSTGRES_USER }} password=${{ secrets.POSTGRES_PASSWORD }} dbname=crawler sslmode=require" up
- name: Build app
run: go build -o fotionapp cmd/server/main.go
- name: SCP to Linode instance ( Binary )
uses: appleboy/scp-action@master
with:
host: ${{ secrets.IP_ADDRESS }}
username: "root"
key: ${{ secrets.SSH_KEY }}
port: 22
source: "fotionapp"
target: "/root"
- uses: zerodays/action-infisical@v1
name: Load .env from Infisical
with:
infisical_token: ${{ secrets.INFISICAL_TOKEN }}
workspace_id: ${{ secrets.INFISICAL_WORKSPACE_ID }}
environment: "production"
- name: SCP to Linode instance ( .env )
uses: appleboy/scp-action@master
with:
host: ${{ secrets.IP_ADDRESS }}
username: "root"
key: ${{ secrets.SSH_KEY }}
port: 22
source: ".env"
target: "/root"
- name: Restart Fotion systemd service
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.IP_ADDRESS }}
username: "root"
key: ${{ secrets.SSH_KEY }}
port: 22
script:
sudo systemctl stop fotion && sudo mv /root/fotionapp /usr/local/bin/fotion &&
sudo mkdir -p /root/fotion && sudo mv /root/.env /root/fotion/.env &&
sudo systemctl restart fotion && sudo systemctl status fotion