DEV Community

Cover image for Building a Pipeline to Deploy Docker Containers to a VPS
Lukas Mauser
Lukas Mauser Subscriber

Posted on • Updated on

Building a Pipeline to Deploy Docker Containers to a VPS

In this follow along, I want to show you a free and convenient way to automatically deploy new versions of your dockerized applications to a virtual private server with a simple git push command.

We will use GitHub Actions to build the Docker image, store it in GitHub container registry (GHCR) and pull it on our VPS with Watchtower.

I will assume that you have access to a VPS already with Docker installed. If you are looking for a cheap VPS provider, I recommend you checkout the linked article. To install Docker, follow the instructions from their official website.

If you are just looking for a cheap way to deploy your containers, I recommend you checkout our service sliplane.io

Let's get started!

Create a Personal Access Token in GitHub

GitHub container registry (GHCR) is a free way to store private Docker images. To access it, we first need to create a personal access token.

  1. Go to the developer settings in GitHub
  2. Click Generate new token and choose Generate new token (classic).
  3. Give the token a descriptive name, set an expiration and select write:packages permissions. Generate the token and make sure to copy it as you'll only see it once.

You can find more detailed instructions in this article from Github.

Create a GitHub Actions Deploy Workflow

We can use GitHub Actions to build our Docker image and push it to GHCR. Inside the repository, that you want to deploy, create a file called release.yml inside .github/workflows.

.
├── .github/
│   └── workflows/
│       └── release.yml
└── ...
Enter fullscreen mode Exit fullscreen mode

Copy the script below into the release.yml file and make sure to update the project-name in step 3 and 4. GitHub will automatically pickup and run this workflow on every new push to the main branch.

name: Build and Push Docker Image
on:
  push:
    branches:
      - main
jobs:
  build-and-push-to-ghcr:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    - name: Log in to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.repository_owner }}
        password: ${{ secrets.PAT }}
    - name: Build Docker image
      run: |
        docker build -t ghcr.io/${{ github.repository_owner }}/project-name:latest .
    - name: Push Docker image
      run: |
        docker push ghcr.io/${{ github.repository_owner }}/project-name:latest

Enter fullscreen mode Exit fullscreen mode

Before you can run this script, you'll also have to store the personal access token in your repository. Go to your repo page, click on Settings > Secrets and variables > Actions, add a new secret named PAT and paste your previously created access token as a value.

Setup Watchtower

Watchtower is a service, that continuously pulls the underlying images from your running containers and compares them to the images in use. If the contents of a pulled image is different from the one in use, the container will be restarted using the new image.

SSH into your VPS:

ssh root@YOURVPSIP
Enter fullscreen mode Exit fullscreen mode

Start Watchtower by simple running the Watchtower Docker container:

docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e REPO_USER="your-github-username" \
  -e REPO_PASS="your-github-pat" \
  containrrr/watchtower \
  --interval 300
Enter fullscreen mode Exit fullscreen mode

Watchtower pulls information about running containers from the docker.sock file, which will be mounted in a volume. By default all running containers are watched, but you can limit watching by passing specific container names:

docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e REPO_USER="your-github-username" \
  -e REPO_PASS="your-github-pat" \
  containrrr/watchtower \
  containername1 containername2 containername3 \
  --interval 300
Enter fullscreen mode Exit fullscreen mode

The REPO_USER and REPO_PASS environment variables can be used to authenticate when pulling from private registries. You can also execute docker login to save your credentials in $HOME/.docker/config.json, and then mount this config file to provide auth credentials to the Watchtower container:

docker run -d \
  --name watchtower \
  -v $HOME/.docker/config.json:/config.json \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --interval 300
Enter fullscreen mode Exit fullscreen mode

The --interval flag specifies, how often new versions will be pulled, in this case every 300 seconds/ 5 minutes.

Watchtower can be pretty heavy on bandwidth. Make sure to fine tune it to only watch required containers and set the interval according to your needs.

If you want to deploy a repository for the first time, you need to manually pull and run the image once. Login to GHCR with

docker login ghcr.io -u USERNAME --password-stdin
Enter fullscreen mode Exit fullscreen mode

and use the personal access token to authenticate.

Pull your image with

docker pull ghcr.io/NAMESPACE/IMAGE_NAME
Enter fullscreen mode Exit fullscreen mode

And run it using

docker run IMAGENAME
Enter fullscreen mode Exit fullscreen mode

Once it's running, watchtower will automatically refetch new versions from GHCR.

Summary

You can quickly create a deploy pipeline by following these steps:

  1. Setup a VPS and install Docker
  2. Create a PAT in GitHub to access the container registry
  3. Create a release Workflow that builds your docker image and publishes it on GHCR
  4. Run Watchtower to automatically sync new versions of your running containers with GHCR

How to improve this setup?
Obviously this is just the starting point for your container deployment pipeline. You can improve this setup by adding a way to automatically add domain records, manage secrets, view logs and monitoring data, and much more... If you want to save yourself some hassle checkout our service sliplane.io which comes with all of these features out of the box.

Top comments (1)

Collapse
 
code42cate profile image
Jonas Scholz

Nice, but I think the current version of docker/login-action@v2 is v4!