Skip to content

GitHub ​

Practical guide to GitHub, GHCR and GitHub Actions for deploying applications.

GHCR ​

GitHub Container Registry (GHCR) is a container registry service that allows you to store and manage Docker images and OCI artifacts directly in GitHub.

1. Generate a Personal Access Token (PAT) ​

Go to GitHub and generate a PAT with repo and packages permissions.

Log in to GitHub, go to Settings then Developer settings. Under Personal access tokens, choose Tokens (classic) and generate a new token with the selected permissions.

2. Log in to GHCR with Docker ​

shell
echo "<TOKEN>" | docker login ghcr.io -u <USERNAME> --password-stdin

3. Pull and run an image ​

shell
docker pull ghcr.io/<USERNAME>/<IMAGE_NAME>
docker run -d -p <HOST_PORT>:<CONTAINER_PORT> ghcr.io/<USERNAME>/<IMAGE_NAME>

4. Log out of GHCR ​

shell
docker logout ghcr.io

GitHub Actions ​

We will use GitHub Actions with GHCR to build and push our images.

Create the .github/workflows/ folders at the root of the project.

Workflows ​

To build and push Docker images we will use GHCR. For SSH deployment, we will use the GitHub Actions utility appleboy.

GHCR Workflow ​

yaml
# .github/workflows/ghcr.yaml
name: GitHub Container Registry

on:
  push:
    branches:
      - main

jobs:
  ghcr:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      packages: write
      attestations: write
      id-token: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Set lowercase repository owner
        run: echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV

      - name: Build and push Docker image
        run: |
          docker build -t ghcr.io/${{ env.REPO_OWNER }}/<TAG_NAME>:latest ./<APP_CODE>
          docker push ghcr.io/${{ env.REPO_OWNER }}/<TAG_NAME>:latest

      - name: Logout from GHCR
        run: docker logout ghcr.io

Deploy Workflow ​

yaml
# .github/workflows/deploy.yaml
deploy:
  needs: [ghcr]
  runs-on: ubuntu-latest
  steps:
    - name: Deploy using ssh
      uses: appleboy/ssh-action@v1.0.3
      with:
        host: ${{ secrets.SSH_HOST }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_KEY }}
        port: ${{ secrets.SSH_PORT }}
        script: |
          echo ${{ secrets.GHCR_PAT }} | docker login ghcr.io -u ${{ github.REPOSITORY_OWNER }} --password-stdin
          docker pull ghcr.io/${{ env.REPO_OWNER }}/<TAG_NAME>:latest
          docker compose -f compose.prod.yaml up -d
          docker logout ghcr.io

Configure on the VPS ​

Create a compose.prod.yaml file ​

yaml
# compose.prod.yaml
services:
  app:
    image: ghcr.io/<REPO_OWNER>/<TAG_NAME>:latest
    ports:
      - "XXXX:XXXX"

Resources ​