This document explains the setup procedure for automatically deploying Docker containers with GitHub Actions.

Table of Contents

  1. Docker Configuration
  2. GitHub Actions Configuration
  3. Server-Side Configuration
  4. Troubleshooting

Docker Configuration

Dockerfile (Static Site + nginx)

Generates static HTML and serves it with nginx.

FROM node:22-alpine AS builder

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run generate

# nginx for serving static files
FROM nginx:alpine

# Nuxt 3: .output/public
# Nuxt 2: dist
COPY --from=builder /app/.output/public /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

nginx.conf (SPA Configuration)

For SPAs, dynamic routes (such as /item/:id) need to fall back to index.html.

server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    # gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # SPA: fall back to index.html for non-existent paths
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Static file caching
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

docker-compose-prod.yml (Traefik Integration)

services:
  cj_front:
    build:
      context: .
    labels:
      traefik.enable: true
      traefik.http.routers.cj_front.rule: Host(`your-domain.com`)
      traefik.http.routers.cj_front.entrypoints: websecure
      traefik.http.routers.cj_front.tls.certresolver: myresolver
      traefik.http.routers.cj_front.middlewares: gzip-compress
      traefik.http.middlewares.gzip-compress.compress: true
    networks:
      - traefik-network
    restart: always
    ports:
      - "8003:80"

networks:
  traefik-network:
    external: true

GitHub Actions Configuration

.github/workflows/prod.yml

name: prod

on:
  push:
    branches: ['master']

  workflow_dispatch:

  repository_dispatch:
    types: [webhook]

concurrency:
  group: 'pages'
  cancel-in-progress: true

jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy Docker Container
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.REMOTE_HOST }}
          username: ${{ secrets.REMOTE_USER }}
          key: ${{ secrets.REMOTE_KEY }}
          port: ${{ secrets.REMOTE_PORT }}
          script: |
            set -e
            cd /home/${{ secrets.REMOTE_USER }}/cj_front
            git pull
            docker compose -f docker-compose-prod.yml build
            docker compose -f docker-compose-prod.yml down
            docker compose -f docker-compose-prod.yml up -d
            docker system prune -a --volumes -f

Registering GitHub Secrets

Register using GitHub CLI:

gh secret set REMOTE_HOST --body "xxx.xxx.xxx.xxx"
gh secret set REMOTE_USER --body "username"
gh secret set REMOTE_PORT --body "22"
gh secret set REMOTE_KEY  ~/.ssh/your_key/id_rsa

Or from the GitHub Web UI: Settings -> Secrets and variables -> Actions -> New repository secret

Secret NameDescription
REMOTE_HOSTServer IP address
REMOTE_USERSSH username
REMOTE_KEYSSH private key (full content)
REMOTE_PORTSSH port (typically 22)

Server-Side Configuration

1. Cloning the Repository

cd /home/username
git clone git@github.com:your-org/your-repo.git cj_front

2. Setting Remote URL to SSH Format

HTTPS format will cause authentication errors when git pull is run from GitHub Actions.

cd /home/username/cj_front
git remote set-url origin git@github.com:your-org/your-repo.git

3. Adding GitHub Host Key

ssh-keyscan github.com >> ~/.ssh/known_hosts

4. Registering SSH Public Key on GitHub

Check the server’s public key:

cat ~/.ssh/id_rsa.pub

If no key exists, generate one:

ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

Register the public key on GitHub:

  • Account level (recommended): GitHub -> Settings -> SSH and GPG keys -> New SSH key
  • Repository level: Repository -> Settings -> Deploy keys -> Add deploy key

Note: The same SSH key can only be registered to one repository or one account on GitHub. If you need to use it across multiple repositories, register it at the account level.

5. Creating docker-compose-prod.yml

cp docker-compose-prod.example.yml docker-compose-prod.yml
# Change the Host name to the actual domain
vim docker-compose-prod.yml

Troubleshooting

404 Error with nginx (SPA)

If accessing a dynamic route (/item/:id) returns a 404, add the following to nginx.conf:

location / {
    try_files $uri $uri/ /index.html;
}

git pull Error in GitHub Actions

Error: fatal: could not read Username for 'https://github.com' Cause: Repository was cloned using HTTPS format Solution: Change to SSH format

git remote set-url origin git@github.com:your-org/your-repo.git

Error: Host key verification failed Solution: Add GitHub’s host key

ssh-keyscan github.com >> ~/.ssh/known_hosts

Error: Permission denied (publickey) Solution: Register the server’s SSH public key on GitHub


References