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

Build docs developers (and LLMs) love