This document explains the setup procedure for automatically deploying Docker containers with GitHub Actions.
Table of Contents
- Docker Configuration
- GitHub Actions Configuration
- Server-Side Configuration
- 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 Name | Description |
|---|---|
REMOTE_HOST | Server IP address |
REMOTE_USER | SSH username |
REMOTE_KEY | SSH private key (full content) |
REMOTE_PORT | SSH 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