SSH deployment with Github actions
GitHub Actions is a powerful tool. Automation helps make the development process more fun. For instance, you can automatically test code in pull requests before merging. This removes the pain of having to check out the branch and then run the test locally 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 accessible via SSH.
A typical deployment in such a setup would involve logging in to the server via SSH and manually 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
It takes the following parameters.
- Git SSH URL
- The deployment directory, e.g./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 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 them to complete for you to know if they were a success. It is even worse when you have to deploy multiple times a day because you need to SSH to the server to deploy your application after a long day of writing code.
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 on your server.
Let us now ensure secure access to your server. There are two ways you can access SSH on your server. One is via plain passwords (I don’t recommend this), and two are via SSH Keys.
The SSH 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 using SSH to access the server. The challenge is that GitHub Actions uses dynamic environments that are destroyed after completing your action.
Bring Your SSH Keys
As mentioned above, GH Actions uses a dynamic environment. 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?
You must generate the two keys in your local machine to solve that. Be careful not to override your default keys in ~/.ssh/ by typing the key name 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 your public key (id_rsa.pub) and 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. Append it. Now, your server will recognize the Key.
Let us now make GitHub actions act like your local machine. Do you know how to SSH into your server from your laptop? Yes, let us make the machine provisioned by Actions do precisely 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 SSH Keys (private and public keys) to our secrets. We are adding them as secrets 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, paste your Public Key contents there, then click add secret. Go ahead and create a new secret name. We’ll call it SSH_PRIV_KEY. 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 be 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 set up, it is time to use them in your actions. You made it here, and 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 use our secrets in GitHub actions. I will use an example to explain. In this example, we use GitHub actions to build a Nuxt.Js application and copy it to the server via SSH.
The most crucial 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 permissions for the files must be 600; otherwise, 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 must be set in your secrets, or you can replace ${{ secrets.IP}} with your IP. (It is public anyway)
Now, your SSH is set up. Your server will accept connections from this dynamic machine. You can see that the last step is called “Copy builds to server”. Feel free to replace this with your 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 is something not working correctly? Feel free to comment below or DM me @StanleyMasinde_
Happy coding!