๐Ÿณ Deploy Docker on Hetzner with GitHub Actions

Deployment blueprint โ€ข 2026 โ€ข โš™๏ธ GitHub Actions

Advertisement Space - Google AdSense

Key Features

๐Ÿ’ฐ Extremely cost-effective: โ‚ฌ4.15/month for CX11 (1 vCPU, 2GB RAM)
๐Ÿ‡ช๐Ÿ‡บ European data centers (Germany, Finland) - GDPR compliant
โšก Excellent network performance and reliability
๐Ÿ”’ GitHub Container Registry (ghcr.io) is free for public repos
๐Ÿ“ฆ Docker Compose for easy multi-container management
๐Ÿ”„ Zero-downtime deployments with health checks

๐Ÿ“‹ Configuration Files

Copy these files into your project:

.github/workflows/deploy.yml
# .github/workflows/deploy.yml
name: Deploy Docker to Hetzner VPS

on:
  push:
    branches:
      - main

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

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

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

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max

      - name: Deploy to Hetzner VPS
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.HETZNER_HOST }}
          username: ${{ secrets.HETZNER_USERNAME }}
          key: ${{ secrets.HETZNER_SSH_KEY }}
          script: |
            # Login to GitHub Container Registry
            echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

            # Pull latest image
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

            # Stop and remove old container
            docker-compose -f /opt/myapp/docker-compose.yml down || true

            # Start new container
            cd /opt/myapp
            docker-compose up -d

            # Clean up old images
            docker image prune -af

# docker-compose.yml (on server at /opt/myapp/docker-compose.yml)
version: '3.8'

services:
  app:
    image: ghcr.io/YOUR_USERNAME/YOUR_REPO:latest
    container_name: myapp
    restart: unless-stopped
    ports:
      - "80:3000"
      - "443:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
    volumes:
      - ./data:/app/data
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

networks:
  app-network:
    driver: bridge

# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
RUN apk add --no-cache curl
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]

๐Ÿ“ฆ Need all the files?

Download the complete blueprint with README, package.json, and all configuration files.

๐Ÿ“ฅ Browse All Files

Configuration Summary

๐Ÿš€
Framework
Docker
โ˜๏ธ
Cloud Provider
Hetzner Cloud
โš™๏ธ
Deployment Tool
GitHub Actions
๐Ÿ’ฐ
Pricing
Extremely competitive pricing, hourly billing

โ˜๏ธ About Hetzner Cloud

Cost-effective European cloud provider

โœ“ Best For
Cost-sensitive projects, European data residency, VPS hosting
๐Ÿ’ฐ Pricing
Extremely competitive pricing, hourly billing

Available Services:

โœ“ Cloud Servers - Virtual Machines
โœ“ Load Balancers - Traffic Distribution
โœ“ Volumes - Block Storage
โœ“ Networks - Private Networking
โœ“ Floating IPs - Flexible IPs
โœ“ Snapshots - Backups
โœ“ Firewalls - Security
โœ“ Object Storage - S3-compatible
โœ“ Dedicated Servers - Bare Metal

โœ… Prerequisites

Make sure you have these ready before starting:

โœ“ Hetzner Cloud account
โœ“ GitHub account with repository admin access
โœ“ SSH client installed locally
โœ“ Basic knowledge of Docker and Linux
โœ“ Domain name (optional, for HTTPS with Caddy/nginx)

๐Ÿš€ Step-by-Step Implementation

Follow these steps to deploy your Docker application:

  1. 1
    Create Hetzner Cloud account at https://www.hetzner.com/cloud
  2. 2
    Create a new VPS (CX11 for โ‚ฌ4.15/month is sufficient to start)
  3. 3
    Choose Ubuntu 22.04 LTS as the OS
  4. 4
    Add your SSH key during server creation
  5. 5
    SSH into server: ssh root@YOUR_SERVER_IP
  6. 6
    Install Docker: curl -fsSL https://get.docker.com | sh
  7. 7
    Install Docker Compose: apt install docker-compose-plugin
  8. 8
    Create app directory: mkdir -p /opt/myapp
  9. 9
    Create docker-compose.yml in /opt/myapp with the configuration above
  10. 10
    Update docker-compose.yml with your GitHub Container Registry image path
  11. 11
    Generate SSH key for GitHub Actions: ssh-keygen -t ed25519 -C "github-actions"
  12. 12
    Add public key to server: cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
  13. 13
    Copy private key for GitHub Secrets: cat ~/.ssh/id_ed25519
  14. 14
    In GitHub repo: Settings โ†’ Secrets โ†’ Actions โ†’ New repository secret
  15. 15
    Add HETZNER_HOST (your server IP)
  16. 16
    Add HETZNER_USERNAME (usually "root")
  17. 17
    Add HETZNER_SSH_KEY (the private key content)
  18. 18
    Create .github/workflows/deploy.yml
  19. 19
    Update IMAGE_NAME in workflow to match your repo
  20. 20
    Commit and push to main branch
  21. 21
    Monitor deployment in GitHub Actions
  22. 22
    Access your app at http://YOUR_SERVER_IP

๐Ÿ’ก Additional Notes & Tips

๐Ÿ“š About This Stack

๐Ÿณ Docker

Containerization platform for consistent deployments

๐Ÿ‡ช๐Ÿ‡บ Hetzner

Cost-effective European cloud provider

โš™๏ธ GitHub Actions

CI/CD automation directly in GitHub

๐Ÿท๏ธ Tags & Keywords

Docker Hetzner Hetzner Cloud GitHub Actions CI/CD DevOps Infrastructure as Code Docker Deployment Hetzner Hosting Production Ready 2026

Advertisement Space - Google AdSense