Skip to content

Docker Support ​

Deploy applications using Docker containers with Gokku.

Overview ​

Gokku uses Docker containers for all applications with blue-green deployment for zero-downtime updates.

All applications run in Docker containers regardless of language:

  • Go - Compiled in container
  • Python - Interpreted in container
  • Node.js - Interpreted in container
  • Ruby - Interpreted in container

Configuration ​

All applications use Docker by default. Configure your app:

yaml
apps:
  my-app:
    lang: python
    path: ./app
    entrypoint: main.py

Automatic Dockerfile Generation ​

If no Dockerfile exists, Gokku generates one based on your language:

Python ​

yaml
apps:
  flask-app:
    lang: python
    path: .
    entrypoint: app.py

Generated Dockerfile:

dockerfile
FROM python:3.11-slim

WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/*

COPY requirements.txt* ./
RUN if [ -f requirements.txt ]; then pip install --no-cache-dir -r requirements.txt; fi

COPY . .

EXPOSE ${PORT:-8080}

CMD ["python", "app.py"]

Node.js ​

yaml
apps:
  node-app:
    lang: nodejs
    path: .
    entrypoint: index.js

Generated Dockerfile:

dockerfile
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE ${PORT:-8080}

CMD ["node", "index.js"]

Go (with Docker) ​

yaml
apps:
  go-app:
    path: .

Generated Dockerfile:

dockerfile
FROM golang:1.25-alpine AS builder

WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/app .

EXPOSE ${PORT:-8080}

CMD ["./app"]

Custom Dockerfile ​

Use your own Dockerfile:

yaml
apps:
  my-app:
    dockerfile: ./Dockerfile

Example: Python with System Dependencies ​

dockerfile
FROM python:3.11-slim

# Install system packages
RUN apt-get update && apt-get install -y \
    gcc \
    ffmpeg \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE ${PORT:-8080}

CMD ["gunicorn", "--bind", "0.0.0.0:${PORT:-8080}", "app:app"]

Example: Multi-Stage Build ​

dockerfile
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist

EXPOSE ${PORT:-8080}

CMD ["node", "dist/server.js"]

Base Images ​

Configure Global Base Images ​

yaml
docker:
  base_images:
    go: "golang:1.25-alpine"
    python: "python:3.11-slim"
    nodejs: "node:20-alpine"

Per-App Base Image ​

yaml
apps:
  ml-service:
    lang: python
    image: "python:3.11"  # Full image, not slim

From Private Registry ​

yaml
docker:
  registry: "registry.example.com"

apps:
  api:
    image: "registry.example.com/python:3.11-custom"

Login on server first:

bash
ssh ubuntu@server "docker login registry.example.com"

Custom Dockerfiles ​

Gokku supports custom Dockerfiles for advanced use cases.

python 3.11
ffmpeg 8.0

Gokku generates:

dockerfile
FROM python:3.11-slim

WORKDIR /app

# Install dependencies
RUN apt-get update && apt-get install -y curl git build-essential && rm -rf /var/lib/apt/lists/*

# Copy application
COPY . .

EXPOSE ${PORT:-8080}

CMD ["python", "app.py"]

.tool-versions:

python 3.11
ffmpeg 8.0
whispercpp 1.5.0

Generated Dockerfile includes plugin installation:

dockerfile
# Install custom dependencies
RUN apt-get update && apt-get install -y ffmpeg
# ...

Image Management ​

Tagging ​

Gokku automatically tags images:

my-app:release-1
my-app:release-2
my-app:release-3

Each deployment creates a new tag.

Keep Images ​

Configure how many images to keep:

yaml
apps:
  my-app:
    deployment:
      keep_images: 10  # Keep last 10 images

Old images are automatically pruned after deployment.

Manual Cleanup ​

bash
# List images
ssh ubuntu@server "docker images | grep my-app"

# Remove specific image
ssh ubuntu@server "docker rmi my-app:release-5"

# Remove all unused images
ssh ubuntu@server "docker image prune -a"

Port Mapping ​

Docker maps: 8080:8080

Custom Ports ​

Set via environment variables:

bash
gokku config set PORT=8081 --app api --env production -a api-production

Then redeploy.

Container Management ​

View Running Containers ​

bash
ssh ubuntu@server "docker ps | grep my-app"

View Logs ​

bash
# Using CLI
gokku logs -a my-app-production -f

# Or directly
ssh ubuntu@server "docker logs -f my-app-blue"

Restart Container ​

bash
# Using CLI
gokku restart -a my-app-production

# Or directly
ssh ubuntu@server "docker restart my-app-blue"

Stop Container ​

bash
ssh ubuntu@server "docker stop my-app-blue"

Start Container ​

bash
ssh ubuntu@server "docker start my-app-blue"

Access Container Shell ​

bash
ssh ubuntu@server "docker exec -it my-app-blue /bin/sh"

Inspect Container ​

bash
ssh ubuntu@server "docker inspect my-app-blue"

Environment Variables ​

All environment variables are passed to the container:

bash
# Set variable
gokku config set DATABASE_URL=postgres://... -a api-production

# Restart container to pick up changes
gokku restart -a api-production

Health Checks ​

Add health check to Dockerfile:

dockerfile
FROM python:3.11-slim

WORKDIR /app
COPY . .

EXPOSE ${PORT:-8080}

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:${PORT:-8080}/health')" || exit 1

CMD ["python", "app.py"]

Check health status:

bash
ssh ubuntu@server "docker inspect --format='{{.State.Health.Status}}' api-blue"

Volumes (Limited Support) ​

Gokku doesn't currently support Docker volumes in config. Workarounds:

Option 2: Host Bind Mount (Manual) ​

Modify the hook template (advanced users):

bash
docker run -d \
  --name $APP_NAME-blue \
  -v /data/uploads:/app/uploads \
  -p $PORT:$PORT \
  $IMAGE_NAME

Option 3: Separate Data Container ​

Run database/redis outside Gokku:

bash
# On server
docker run -d \
  --name postgres \
  -v /data/postgres:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:15

Networking ​

Container to Container ​

Use Docker host IP (172.17.0.1) or host.docker.internal.

Blue-Green Deployment ​

Gokku uses blue-green deployment for zero-downtime updates:

How It Works ​

  1. New Deployment: Code is deployed to the green environment
  2. Health Check: System verifies the green environment is healthy
  3. Traffic Switch: Traffic is switched from blue to green
  4. Cleanup: Old blue environment is stopped and cleaned up

Enable Zero-Downtime ​

bash
gokku config set ZERO_DOWNTIME=true -a <app>-<env>

Container Naming ​

  • Active Container: <app-name>-blue
  • New Container: <app-name>-green

For more details, see Blue-Green Deployment Guide.

Troubleshooting ​

Build Failed ​

Check deploy logs:

bash
ssh ubuntu@server "cat /opt/gokku/apps/my-app/production/deploy.log"

Container Won't Start ​

Check Docker logs:

bash
ssh ubuntu@server "docker logs my-app-blue"

Out of Disk Space ​

Clean up old images:

bash
ssh ubuntu@server "docker system prune -a"

Image Pull Failed ​

Check registry login:

bash
ssh ubuntu@server "docker login registry.example.com"

Advanced ​

Custom Docker Run Options ​

To add custom Docker run options, modify the hook template:

/opt/gokku/hooks/post-receive-docker.template

Add options to the docker run command.

Docker Compose (Not Supported) ​

Gokku uses docker run, not docker-compose. For multi-container apps:

  1. Deploy each service separately
  2. Or use a process manager inside one container (supervisord)

Private Registry ​

yaml
docker:
  registry: "registry.example.com"

apps:
  api:
    image: "registry.example.com/python:3.11"

Login on server:

bash
ssh ubuntu@server "docker login registry.example.com"

Next Steps ​

Released under the MIT License.