SSH deployment with Github actions
--
GitHub actions is a powerful tool. It helps makes the development process more fun through automations. For instance, you can automatically test code in pull requests before merging. This takes away the pain of having to checkout the branch, then run the test locally for you to make sure the changes being introduced do not break your application.
Deployment pitfalls
This article assumes you are deploying to a single machine with a known IP address that is accessible via SSH.
A typical deployment in such a setup would involve; logging in to the server and running your deployment commands. To move fast, you might have created a script that you can run to deploy the application. Here is a sample script I use in my deployment
I takes following parameters
- Git SSH URL
- The deployment directory eg. /var/www
- The deployment directory name e.g example.com
- The deployment branch which defaults to main
This seems efficient but it is far from it. For that you work, you need to add the server’s SSH public key to your GitHub account for it to be able to clone. If your deployments take time you need to sit and wait for it to complete for you to know if it was a success. It is even worse when you have to deploy multiple times a day because after a long day of writing code you need to SSH to the server to deploy your application.
Adding some fun deployment with GitHub actions
For this to be fun, we need to solve two problems here;
- GitHub actions to securely access your server
- Run your builds on GitHub actions to reduce unnecessary load in your server
Let us now ensure secure access to your server. There are two ways you can access SSH into your server. One via plain passwords (I don’t recommend this) and two via SSH Keys.
The SSH key way involves adding the SSH public key of the machine you are using to SSH into the server in the file located at ~/.ssh/authorized_keys
. This is for the user who will be using SSH to access the server. The challenge is, GitHub actions uses a dynamic environments which are destroyed after your action has completed.
Bring Your own Keys
As mentioned above, ‘GH actions’ use dynamic environments hence running the usual ssh-key-gen won’t cut it because you will need to add a new key to your server’s authorized keys every time. That doesn’t sound fun, does it?
To solve that, you will need to generate the two keys in your local machine. Be careful not to override your default keys in ~/.ssh/ by typing the name of the key when asked for the file on which to save the key. On the passphrase, just press enter. We are trying to move away from passwords, aren’t we?
After the command, you can see both your public key (id_rsa.pub) and your private key (id_rsa). Please note that the names come from what I name my keys. In the screenshot above, you can see my key is named id_rsa.
Now SSH into your VPS and paste the contents of id_rsa.pub into your server’s ~/.ssh/authorized_keys file. Do not overwrite anything in the file just append it. Now your server will recognize the Key.
Let us now make GitHub actions act like your local machine. Do you know how you can just SSH into your server from your laptop? Yes, let us make the machine provisioned by Actions do exactly that.
On GitHub, under your repository settings, Look for Actions under the security tab and click on it.
Then click on create new secret. We want to add both SSH Keys to our secrets (private key and public key). The reason we are adding them as secrets, is because they are meant to be secrets. No one including you can view the content s of the secret.
Name your first secret SSH_PUB_KEY and paste your public key contents there then click add secret. Go ahead and create a new secret name it SSH_PRIV_KEY and paste the contents of your private key and click add secret. Using the same process above, create a new secret and name it USER_IP. The content of this will the <sshuser>@<your server ip> e.g john@192.168.0.1. Make sure this is the user you use to access your VPS.
Now that your secrets are setup, it is now time to use them in your actions. You made it here you deserve a cup of coffee! Not on me of course.
Wrapping things up.
Now that the boring configuration part is out of the way, let us now use our secrets in GitHub actions. I will use an example to explain. In this example, we are using GitHub actions to build a Nuxt.Js application and copying it to the server via SSH
The most important step for this action is the “Setup SSH” step. Here is what’s happening there
- Create the SSH directory
- Add our SSH private Key from secrets to the default ssh private key location(This is important so that we don’t need to pass in the location of both keys in our ssh command)
- Repeat step 2 but with our Public Key from secrets.
- The permisions for the files need to be 600 or else SSH will complain. You can probably write a better script to do this recursively 😉
- Same step as above
- ssh-keyscan to add your key to known hosts. Your IP address needs to be set in your secrets or you can replace ${{ secrets.IP}} with your IP. (It is public anyway)
Now your SSH is setup. You server will accept connections from this dynamic machine. You can see that last step called “Copy builds to server”. Feel free to replace this with your own logic. In the example above, it uses RSYNC to copy the build files to the server. Remember what the USER_IP secret contains? it is simply user@ip.
Found a mistake or something is not working correctly? Feel free to comment below or DM me @StanleyMasinde_
Happy coding!