Running Gordon in a Container
Run Gordon itself inside a Docker or Podman container for isolated deployments.
What You'll Learn
- Running Gordon in Docker with socket access
- Running Gordon in Podman (rootless)
- Configuration via environment variables
- Volume management for persistence
Prerequisites
- Docker or Podman installed on the host
- Domain name pointing to your server
- Basic understanding of container networking
Why Run Gordon in a Container?
- Isolation: Gordon runs in its own environment
- Portability: Same setup across different hosts
- Updates: Easy version management with image tags
- Orchestration: Can be managed by Docker Compose, Kubernetes, etc.
Docker
Quick Start
docker run -d \
--name gordon \
-p 80:8080 \
-p 5000:5000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v gordon-data:/data \
-e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \
ghcr.io/bnema/gordon:latest
With Configuration File
Create gordon.toml:
[server]
port = 8080
registry_port = 5000
gordon_domain = "gordon.mydomain.com"
[routes]
"app.mydomain.com" = "myapp:latest"
"api.mydomain.com" = "myapi:latest"
[logging]
level = "info"
Run with mounted config:
docker run -d \
--name gordon \
-p 80:8080 \
-p 5000:5000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v gordon-data:/data \
-v $(pwd)/gordon.toml:/etc/gordon/gordon.toml:ro \
ghcr.io/bnema/gordon:latest
Docker Compose
services:
gordon:
image: ghcr.io/bnema/gordon:latest
container_name: gordon
restart: unless-stopped
ports:
- "80:8080"
- "5000:5000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- gordon-data:/data
- ./gordon.toml:/etc/gordon/gordon.toml:ro
environment:
- GORDON_LOGGING_LEVEL=info
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
gordon-data:
Start with:
docker compose up -d
Podman
Socket Setup
Podman uses a different socket path. Enable the Podman socket first:
# For rootless Podman (user service)
systemctl --user enable --now podman.socket
# Socket path: /run/user/$(id -u)/podman/podman.sock
For system-wide (root) Podman:
sudo systemctl enable --now podman.socket
# Socket path: /run/podman/podman.sock
Quick Start (Rootless)
podman run -d \
--name gordon \
-p 8080:8080 \
-p 5000:5000 \
--security-opt label=disable \
-v /run/user/$(id -u)/podman/podman.sock:/var/run/docker.sock \
-v gordon-data:/data \
-e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \
-e DOCKER_HOST=unix:///var/run/docker.sock \
ghcr.io/bnema/gordon:latest
Note:
--security-opt label=disableis required for socket access with SELinux.
Quick Start (Root/System)
sudo podman run -d \
--name gordon \
-p 80:8080 \
-p 5000:5000 \
--security-opt label=disable \
-v /run/podman/podman.sock:/var/run/docker.sock \
-v gordon-data:/data \
-e GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com \
-e DOCKER_HOST=unix:///var/run/docker.sock \
ghcr.io/bnema/gordon:latest
Podman Compose (Quadlet)
Create ~/.config/containers/systemd/gordon.container:
[Unit]
Description=Gordon Container Platform
After=podman.socket
[Container]
Image=ghcr.io/bnema/gordon:latest
ContainerName=gordon
PublishPort=8080:8080
PublishPort=5000:5000
Volume=/run/user/%U/podman/podman.sock:/var/run/docker.sock
Volume=gordon-data:/data
Environment=GORDON_SERVER_REGISTRY_DOMAIN=registry.mydomain.com
Environment=DOCKER_HOST=unix:///var/run/docker.sock
SecurityLabelDisable=true
[Service]
Restart=always
[Install]
WantedBy=default.target
Enable and start:
systemctl --user daemon-reload
systemctl --user enable --now gordon
Configuration Reference
Required Volumes
| Mount | Purpose |
|---|---|
/var/run/docker.sock |
Access to container runtime (Docker or Podman socket) |
/data |
Registry storage, logs, environment files |
Optional Volumes
| Mount | Purpose |
|---|---|
/etc/gordon/gordon.toml |
Configuration file |
Ports
| Port | Purpose |
|---|---|
8080 |
HTTP proxy (map to 80 on host) |
5000 |
Container registry |
Environment Variables
All configuration options can be set via environment variables with GORDON_ prefix:
| Variable | Description | Example |
|---|---|---|
GORDON_SERVER_PORT |
Proxy port | 8080 |
GORDON_SERVER_REGISTRY_PORT |
Registry port | 5000 |
GORDON_SERVER_REGISTRY_DOMAIN |
Registry domain (required) | registry.mydomain.com |
GORDON_SERVER_DATA_DIR |
Data directory | /data |
GORDON_LOGGING_LEVEL |
Log level | debug, info, warn, error |
GORDON_REGISTRY_AUTH_ENABLED |
Enable registry auth | true |
GORDON_REGISTRY_AUTH_USERNAME |
Registry username | admin |
Note: For registry passwords, using plain
passwordis deprecated. Usepassword_hashwith a secrets backend instead. See Registry Auth for secure setup.
Using Pass Secrets Backend
To use the pass secrets backend in a container, you need to mount GPG keys and the password store.
Prerequisites on Host
# Install pass and initialize (if not already done)
sudo apt install pass gnupg
gpg --gen-key
pass init <your-gpg-key-id>
# Store Gordon secrets
pass insert gordon/auth/password_hash
pass insert gordon/auth/token_secret
Custom Dockerfile
The default Gordon image doesn't include pass. Create a custom Dockerfile:
FROM ghcr.io/bnema/gordon:latest
USER root
RUN apk add --no-cache pass gnupg
USER gordon
Build it:
docker build -t gordon-with-pass .
Docker Run with Pass
docker run -d \
--name gordon \
-p 80:8080 \
-p 5000:5000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v gordon-data:/data \
-v $HOME/.gnupg:/home/gordon/.gnupg:ro \
-v $HOME/.password-store:/home/gordon/.password-store:ro \
-v $(pwd)/gordon.toml:/etc/gordon/gordon.toml:ro \
gordon-with-pass
Docker Compose with Pass
services:
gordon:
build:
context: .
dockerfile: Dockerfile.gordon
container_name: gordon
restart: unless-stopped
ports:
- "80:8080"
- "5000:5000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- gordon-data:/data
- ./gordon.toml:/etc/gordon/gordon.toml:ro
# GPG and pass mounts
- ~/.gnupg:/home/gordon/.gnupg:ro
- ~/.password-store:/home/gordon/.password-store:ro
environment:
- GORDON_SECRETS_BACKEND=pass
volumes:
gordon-data:
Configuration
[auth]
enabled = true
secrets_backend = "pass"
token_secret = "gordon/auth/token_secret"
# Optional: enable password auth for interactive login
# username = "admin"
# password_hash = "gordon/auth/password_hash"
GPG Agent (Optional)
For GPG keys with passphrases, you may need to forward the GPG agent socket:
-v $(gpgconf --list-dirs agent-socket):/home/gordon/.gnupg/S.gpg-agent:ro
Or use a passphrase-less GPG key for automated deployments.
Troubleshooting Pass
"gpg: decryption failed: No secret key"
The GPG private key isn't accessible. Verify mounts:
docker exec gordon gpg --list-secret-keys
"pass: command not found"
Use the custom Dockerfile with pass installed.
Permission denied on .gnupg
Ensure the gordon user (UID 1000) can read the mounted directories:
# On host, check permissions
ls -la ~/.gnupg
ls -la ~/.password-store
Security Considerations
Docker Socket Access
Mounting the Docker/Podman socket grants Gordon full access to the container runtime. This is required for Gordon to manage containers but has security implications:
- Gordon can create, stop, and remove any container
- Gordon can access volumes and networks
- Consider network policies to restrict Gordon's container access
Recommendations
- Use a dedicated host: Run Gordon on a dedicated server or VM
- Limit network exposure: Only expose ports 80/443 publicly
- Use TLS: Put Gordon behind a reverse proxy with TLS (Cloudflare, Caddy, nginx)
- Enable registry auth: Always enable authentication for the registry
Troubleshooting
"Cannot connect to Docker daemon"
Verify socket mount:
# Docker
docker exec gordon ls -la /var/run/docker.sock
# Podman - check socket exists
ls -la /run/user/$(id -u)/podman/podman.sock
"Permission denied" on socket
For Podman, ensure --security-opt label=disable is set.
For Docker, ensure the gordon user inside the container can access the socket:
# Check socket permissions on host
ls -la /var/run/docker.sock
Container can't bind to port 80
The Gordon container runs as non-root user gordon. Use port 8080 inside the container and map to 80 on the host:
-p 80:8080 # Host port 80 -> Container port 8080
Health check failing
# Check Gordon logs
docker logs gordon
# Test health endpoint manually
docker exec gordon wget -q -O- http://localhost:8080/health