Deployment Overview

Gordon deploys containers when you push images to its built-in registry. Three deployment methods are available depending on your workflow and infrastructure.

gordon push --build --remote is the simplest way to build, push, and deploy from CI/CD pipelines.

  • Single command: build + push + deploy
  • Single secret (GORDON_TOKEN): auto-exchanges for a short-lived registry token
  • Auto-detects version from CI environment ($GITHUB_REF, $CI_COMMIT_TAG, $BUILD_SOURCEBRANCH, or git describe)
  • Chunked uploads (50MB chunks) — works behind Cloudflare and restrictive proxies
gordon push --build --remote https://gordon.example.com --no-confirm

All Deployment Methods

Method Best For Secrets Needed Registry Access Deploy Control
gordon push (Recommended) CI/CD pipelines 1 (GORDON_TOKEN) Via gordon domain (HTTPS) Explicit (CLI-triggered)
docker push Simple setups, existing Docker workflows 2 (username + token) Via gordon domain (HTTPS) Automatic (event-based)
Docker labels + auto-route GitOps, zero-config deploys 2 (username + token) Via gordon domain (HTTPS) Automatic (label-driven)

The Gordon CLI handles authentication, image building, and deployment in a single step.

  • Single token handles everything: admin API access + registry auth via automatic token exchange
  • Version tag auto-detected from $GITHUB_REF, $CI_COMMIT_TAG, $BUILD_SOURCEBRANCH, or git describe
  • Use --no-deploy for push-only workflows (useful for staging images without triggering deployment)
  • Requires the Gordon binary on the CI runner
# Build, push, and deploy
gordon push --build --remote https://gordon.example.com --no-confirm

# Push only, no deploy
gordon push --build --remote https://gordon.example.com --no-deploy --no-confirm

Method 2: docker push

Standard Docker workflow — no Gordon binary required on the runner.

  • Use docker login, docker build, and docker push as usual
  • Gordon auto-deploys when it receives the image (event-based, no explicit trigger needed)
  • No Gordon binary needed on the CI runner
  • Registry endpoint is gordon.example.com (not a separate registry host)
echo "$GORDON_TOKEN" | docker login -u ci-bot --password-stdin gordon.example.com
docker build -t gordon.example.com/myapp:v1.2.0 .
docker push gordon.example.com/myapp:v1.2.0

Method 3: Docker labels + auto-route

Add a gordon.domain label to your image and Gordon creates the route automatically on push.

  • Add gordon.domain=app.example.com label to your Dockerfile
  • Push image — Gordon creates or updates the route without manual config
  • Gated by the auto_route_allowed_domains allowlist in gordon.toml
  • Best for GitOps workflows where routes are defined alongside the application
LABEL gordon.domain="app.example.com"

See Auto-Route for configuration details.

Registry Access

Gordon's registry is served through the main gordon domain over HTTPS on port 443. The internal registry port (5000) is never exposed externally. All push methods use https://gordon.example.com/v2/....

Network Topologies

Setup Configuration Use Case
Public (default) auth.enabled = true Hosted CI runners (GitHub Actions, GitLab CI)
Tailscale only registry_allowed_ips = ["100.64.0.0/10"] Self-hosted runners in Tailnet
Localhost only auth.enabled = false Local development, single-machine deploys

Token Setup

See the examples below for the right scopes for each workflow.

# Minimum scope for gordon push — route lookup + registry push
# Server auto-deploys when it receives the image
gordon auth token generate \
  --subject ci-bot \
  --scopes "push,pull,admin:routes:read" \
  --expiry 90d

# With explicit CLI-managed deploy (adds config:write for deploy control)
gordon auth token generate \
  --subject ci-bot \
  --scopes "push,pull,admin:routes:read,admin:config:write" \
  --expiry 90d

# Scoped to a specific repository
gordon auth token generate \
  --subject ci-bot \
  --repo myapp \
  --scopes "push,pull,admin:routes:read" \
  --expiry 90d

# For docker push — registry scopes only
gordon auth token generate \
  --subject ci-bot \
  --scopes "push,pull" \
  --expiry 90d

Set the generated token as GORDON_TOKEN in your CI environment.

Version Strategies

Latest Tag

Always deploy the most recent build:

docker tag myapp gordon.example.com/myapp:latest
docker push gordon.example.com/myapp:latest
[routes]
"app.example.com" = "myapp:latest"

Semantic Versioning

Pin routes to specific versions and update config to roll forward:

docker tag myapp gordon.example.com/myapp:v2.1.0
docker push gordon.example.com/myapp:v2.1.0
[routes]
"app.example.com" = "myapp:v2.1.0"

To deploy a new version, update the tag in gordon.toml and push the new image.

Git SHA Tags

Tag with commit hash for full traceability:

VERSION=$(git rev-parse --short HEAD)
docker tag myapp gordon.example.com/myapp:$VERSION
docker push gordon.example.com/myapp:$VERSION

Zero-Downtime Deployment

Gordon performs zero-downtime deployments by default:

  1. New container starts while the old one is still running
  2. Health check waits for the new container to become ready
  3. Traffic switches to the new container
  4. Old container stops after traffic moves
Timeline ─────────────────────────────────────────>

Old Container:  [═══════════════════]
                                    ↓ stop
New Container:           [═════════════════════════>
                         ↑ start    ↑ traffic routed