How To Deploy A Docker Image To A Server Using GitHub Actions

No Comments
Modified: 16.07.2023

Do you want to learn how to automatically deploy your docker image to your server and, in the same run, update your container using GitHub Actions? In this guide, we will do exactly that! We will first store the image inside GitHub’s Container Registry and then create an Action to deploy it onto the server whenever we push a change!

  1. Store docker image in GitHub’s Container Registry
  2. Running the container on your server
  3. Automate the deployment of your application using GitHub Actions
  4. Conclusion

You do need a Docker image for this guide. In case you do not have an image yet, check out my last post on how to create one here.

Server icon

VPS Hosting Course

Learn everything you need to know about servers and hosting your own applications!
Don’t want to read? Watch the video instead!

Store docker image in GitHub’s Container Registry

To store our Docker image, we will use the GitHub Container Registry, which allows us to easily access it from anywhere, especially our server. To store an image in the GHCR, we first need to create a GitHub Personal Access Token (PAT). For more details on the scopes you need and other information, you can check out the GitHub Packages Registry guide. If you want to get started quickly, you can use this link, which includes all the required scopes: https://github.com/settings/tokens/new?scopes=write:packages,read:packages,delete:packages

After you click on the link, you need to specify the duration and give the PAT a name, then save the value somewhere secure.

Now, open your terminal and follow these steps from the directory containing the Dockerfile:

  1. Store your PAT inside an environment variable: export CR_PAT=<PAT>
  2. Sign in to the container registry: echo $CR_PAT | docker login ghcr.io -u <username> --password-stdin
  3. Build and upload the container image: docker build . -t ghcr.io/<username>/<image-name>:latest && docker push ghcr.io/<username>/<image-name>:latest

By following these steps, your Docker image will be built and uploaded to the GHCR, and you will be able to access it from your server using the specified image tag. You need to run steps 1 and 2 on your server as well.

Need help or want to share feedback? Join my discord community!

Running the container on your server

The next step is to create a docker-compose.yml file on our server (inside of /home/<username>/<project>), which is a template for our Docker container. This way, we can easily move it to another machine or rebuild the containers. Inside the file, we specify the name of the service, the image, and the ports to use:

services:
  frontend:
    container_name: frontend
    image: ghcr.io/programonaut/svelte-counter:latest
    ports:
      - 80:80

After creating the docker-compose.yml file, we can build the container by running docker compose up -d. This command will start the container in detached mode, meaning it runs in the background. To check if your container can be built. If everything works correctly, you can access it under http://<server-ip>. To learn how to connect it with a domain, check this post here.

KOFI Logo

If this guide is helpful to you and you like what I do, please support me with a coffee!

Automate the deployment of our application using GitHub Actions

Automating the deployment of our application significantly reduces manual work and speeds up the deployment process. Before we create the secrets and the GitHub Action, we need to set up a new SSH key for it. To do so, log in to your server as the user you want to run the action as and follow these steps:

  1. Check that user has access to the directory containing the repository and is able to run docker
  2. Create an SSH key: ssh-keygen -t rsa -b 4096
  3. Copy content of the key file: cat <path/to/private/key>
  4. Add the public key to the authorized_keys file: cat <path/to/public/key> >> ~/.ssh/authorized_keys

After creating the SSH key, we need to create the following secrets in the repository by going to Settings > Secrets and Variables > Actions:

Once the secrets are set up, we can create the following action inside the file .github/workflows/docker-publish.yml. The action first builds the image and pushes it to the registry inside the publish job, then pulls it onto the server and updates the container in the deploy job.

name: publish

on:
  push:
    branches: [ "main" ]

env:
  # Use docker.io for Docker Hub if empty
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.actor }}/<image-name>:latest

jobs:
    publish:
        name: publish image
        runs-on: ubuntu-latest

        steps:
        - uses: actions/checkout@v3
        - name: Login
          run: |
            echo ${{ secrets.PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
        - name: Build and Publish
          run: |
            docker build . --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
            docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

    deploy:
        needs: publish
        name: deploy image
        runs-on: ubuntu-latest

        steps:
        - name: install ssh keys
          # check this thread to understand why its needed:
          # <https://stackoverflow.com/a/70447517>
          run: |
            install -m 600 -D /dev/null ~/.ssh/id_rsa
            echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
            ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
        - name: connect and pull
          run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.WORK_DIR }} && docker compose pull && docker compose up -d && exit"
        - name: cleanup
          run: rm -rf ~/.ssh

This job basically logs into the server, pulls the new version of the image, and then rebuilds the containers.

Conclusion

In this guide, you learned how to use GitHub Actions to automatically deploy a docker image to your server! The action first builds the Docker image, pushes it to GitHub’s Container Registry, then pulls the new version onto the server and updates the container!

If you have any questions feel free to leave a comment, join my discord, or send me an email.

[convertkit form=2303042]

Discussion (0)