Skip to main content

Overview

While gitGost does not currently ship with an official Dockerfile, deploying via Docker is straightforward. This guide provides production-ready Docker configurations.
The gitGost repository does not include a Dockerfile yet. This guide provides a reference implementation you can use.

Creating a Dockerfile

Create a Dockerfile in the root of the gitGost repository:
Dockerfile
# Build stage
FROM golang:1.22-alpine AS builder

# Install git (required for go mod download with private repos)
RUN apk add --no-cache git

# Set working directory
WORKDIR /build

# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download

# Copy source code
COPY . .

# Build with version info
ARG COMMIT_HASH=unknown
ARG BUILD_TIME=unknown
RUN go build -ldflags "-w -s -X main.commitHash=${COMMIT_HASH} -X main.buildTime=${BUILD_TIME}" \
    -o gitgost ./cmd/server

# Runtime stage
FROM alpine:latest

# Install git and ca-certificates (required for GitHub API)
RUN apk add --no-cache git ca-certificates

# Create non-root user
RUN addgroup -g 1000 gitgost && \
    adduser -D -u 1000 -G gitgost gitgost

# Set working directory
WORKDIR /app

# Copy binary from builder
COPY --from=builder /build/gitgost .

# Copy web assets if they exist
COPY --from=builder /build/web ./web

# Change ownership
RUN chown -R gitgost:gitgost /app

# Switch to non-root user
USER gitgost

# Expose default port
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

# Run the server
CMD ["./gitgost"]

Building the Docker Image

1

Build with version info

docker build \
  --build-arg COMMIT_HASH=$(git rev-parse --short HEAD) \
  --build-arg BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
  -t gitgost:latest \
  .
2

Verify the image

docker images | grep gitgost

# Expected output:
# gitgost      latest    abc123def456   2 minutes ago   25MB
3

Test run

docker run --rm -p 8080:8080 \
  -e GITHUB_TOKEN=your_token \
  gitgost:latest

Docker Run Command

For a simple deployment without Docker Compose:
docker run -d \
  --name gitgost \
  --restart unless-stopped \
  -p 8080:8080 \
  -e GITHUB_TOKEN="your_github_token" \
  -e GITGOST_API_KEY="your_api_key" \
  -e SUPABASE_URL="https://your-project.supabase.co" \
  -e SUPABASE_KEY="your_supabase_key" \
  -e PANIC_PASSWORD="your_strong_password" \
  -e NTFY_ADMIN_TOPIC="gitgost-admin-alerts" \
  -e LOG_FORMAT="json" \
  gitgost:latest

Using an .env file

# Create .env file
cat > .env << 'EOF'
GITHUB_TOKEN=your_github_token
GITGOST_API_KEY=your_api_key
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your_supabase_key
PANIC_PASSWORD=your_strong_password
NTFY_ADMIN_TOPIC=gitgost-admin-alerts
LOG_FORMAT=json
EOF

# Run with .env file
docker run -d \
  --name gitgost \
  --restart unless-stopped \
  -p 8080:8080 \
  --env-file .env \
  gitgost:latest

Docker Compose

Basic Configuration

Create docker-compose.yml:
docker-compose.yml
version: '3.8'

services:
  gitgost:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        COMMIT_HASH: ${COMMIT_HASH:-main}
        BUILD_TIME: ${BUILD_TIME:-unknown}
    image: gitgost:latest
    container_name: gitgost
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - GITHUB_TOKEN=${GITHUB_TOKEN}
      - GITGOST_API_KEY=${GITGOST_API_KEY:-}
      - PORT=8080
      - LOG_FORMAT=json
      - READ_TIMEOUT=30s
      - WRITE_TIMEOUT=30s
      - SUPABASE_URL=${SUPABASE_URL:-}
      - SUPABASE_KEY=${SUPABASE_KEY:-}
      - PANIC_PASSWORD=${PANIC_PASSWORD}
      - NTFY_ADMIN_TOPIC=${NTFY_ADMIN_TOPIC:-}
      - NTFY_BASE_URL=${NTFY_BASE_URL:-https://ntfy.sh}
      - SERVICE_URL=${SERVICE_URL:-https://gitgost.leapcell.app}
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
      interval: 30s
      timeout: 3s
      start_period: 5s
      retries: 3
    networks:
      - gitgost-network

networks:
  gitgost-network:
    driver: bridge

Production Configuration with nginx

Create docker-compose.prod.yml:
docker-compose.prod.yml
version: '3.8'

services:
  gitgost:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        COMMIT_HASH: ${COMMIT_HASH:-main}
        BUILD_TIME: ${BUILD_TIME:-unknown}
    image: gitgost:latest
    container_name: gitgost
    restart: unless-stopped
    expose:
      - "8080"
    environment:
      - GITHUB_TOKEN=${GITHUB_TOKEN}
      - GITGOST_API_KEY=${GITGOST_API_KEY:-}
      - PORT=8080
      - LOG_FORMAT=json
      - READ_TIMEOUT=30s
      - WRITE_TIMEOUT=30s
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_KEY=${SUPABASE_KEY}
      - PANIC_PASSWORD=${PANIC_PASSWORD}
      - NTFY_ADMIN_TOPIC=${NTFY_ADMIN_TOPIC}
      - NTFY_BASE_URL=${NTFY_BASE_URL:-https://ntfy.sh}
      - SERVICE_URL=${SERVICE_URL}
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
      interval: 30s
      timeout: 3s
      start_period: 5s
      retries: 3
    networks:
      - gitgost-network

  nginx:
    image: nginx:alpine
    container_name: gitgost-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
      - ./logs:/var/log/nginx
    depends_on:
      - gitgost
    networks:
      - gitgost-network

networks:
  gitgost-network:
    driver: bridge
Create nginx.conf:
nginx.conf
events {
    worker_connections 1024;
}

http {
    upstream gitgost {
        server gitgost:8080;
    }

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=push_limit:10m rate=5r/h;
    limit_req_zone $binary_remote_addr zone=admin_limit:10m rate=10r/m;

    server {
        listen 80;
        server_name _;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name _;

        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;

        # Security headers
        add_header Strict-Transport-Security "max-age=31536000" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-Frame-Options "DENY" always;

        # Max body size
        client_max_body_size 100M;

        # Push endpoints (rate limited)
        location ~ ^/v1/gh/.*/git-receive-pack$ {
            limit_req zone=push_limit burst=2 nodelay;
            proxy_pass http://gitgost;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_read_timeout 300s;
            proxy_connect_timeout 75s;
        }

        # Admin endpoints (strict rate limiting)
        location /admin/ {
            limit_req zone=admin_limit burst=5 nodelay;
            proxy_pass http://gitgost;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # All other endpoints
        location / {
            proxy_pass http://gitgost;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}

Usage

1

Create .env file

cp .env.example .env
# Edit .env with your configuration
2

Start services

docker-compose up -d
3

View logs

docker-compose logs -f gitgost
4

Stop services

docker-compose down

Volume Mounts

gitGost does not require persistent volumes by design. All git operations are ephemeral.
Do not mount /tmp or other writable directories unless you understand the security implications. gitGost is designed to be stateless.

Optional: Persistent Logs

If you want to persist logs outside the container:
services:
  gitgost:
    # ... other config ...
    volumes:
      - ./logs:/app/logs
    environment:
      - LOG_FORMAT=json

Port Mappings

Container PortHost PortPurpose
80808080 (or custom)Main HTTP server
gitGost uses a single port for all operations (health, metrics, git-receive-pack, admin, etc.).

Multi-Architecture Builds

To build for multiple platforms (e.g., ARM64 for Raspberry Pi):
# Enable Docker buildx
docker buildx create --use

# Build for multiple architectures
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --build-arg COMMIT_HASH=$(git rev-parse --short HEAD) \
  --build-arg BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
  -t your-registry/gitgost:latest \
  --push \
  .

Container Registry

Push to Docker Hub

# Tag the image
docker tag gitgost:latest yourusername/gitgost:latest
docker tag gitgost:latest yourusername/gitgost:$(git rev-parse --short HEAD)

# Push
docker push yourusername/gitgost:latest
docker push yourusername/gitgost:$(git rev-parse --short HEAD)

Push to GitHub Container Registry

# Login to GHCR
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin

# Tag
docker tag gitgost:latest ghcr.io/livrasand/gitgost:latest

# Push
docker push ghcr.io/livrasand/gitgost:latest

Troubleshooting

Container Exits Immediately

# Check logs
docker logs gitgost

# Common causes:
# - Missing GITHUB_TOKEN
# - Invalid environment variables
# - Port 8080 already in use on host

Health Check Failing

# Test manually
docker exec gitgost wget --no-verbose --tries=1 --spider http://localhost:8080/health

# Check service status
curl http://localhost:8080/health

Build Fails

# Clean build cache
docker builder prune -a

# Rebuild without cache
docker build --no-cache -t gitgost:latest .

Security Best Practices

Use Secrets

Use Docker secrets or environment files instead of hardcoding credentials in docker-compose.yml.

Run as Non-Root

The Dockerfile provided runs as a non-root user (UID 1000) for security.

Network Isolation

Use custom networks to isolate gitGost from other containers.

TLS Termination

Always use a reverse proxy (nginx, Traefik, Caddy) with TLS for production.

Next Steps

Environment Variables

Complete reference of all configuration options

Configuration

Advanced configuration and production hardening

Requirements

System requirements and prerequisites