Skip to content

Docker Compose OAuth

This example deploys FrontMark as a static Nginx site and adds the dedicated OAuth/API proxy container for Sveltia CMS. The public CMS URL stays:

https://sigmax.dev/frontmark/admin/

Build the static image and the OAuth sidecar:

make image IMAGE=registry.example.com/frontmark:latest
make image_oauth OAUTH_IMAGE=registry.example.com/frontmark-oauth:latest

The generated CMS backend should contain:

api_root: https://sigmax.dev/frontmark/api/forgejo/v1
base_url: https://sigmax.dev/frontmark/oauth/forgejo
auth_methods:
  - oauth
  - token
services:
  traefik:
    image: traefik:v3.1
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.letsencrypt.acme.email=admin@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-letsencrypt:/letsencrypt
    networks:
      - frontmark

  frontmark:
    image: registry.example.com/frontmark:latest
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.routers.frontmark.rule=Host(`sigmax.dev`) && PathPrefix(`/frontmark`)
      - traefik.http.routers.frontmark.entrypoints=websecure
      - traefik.http.routers.frontmark.tls.certresolver=letsencrypt
      - traefik.http.routers.frontmark.priority=10
      - traefik.http.services.frontmark.loadbalancer.server.port=4321
    networks:
      - frontmark

  frontmark-oauth:
    image: registry.example.com/frontmark-oauth:latest
    restart: unless-stopped
    environment:
      PUBLIC_GIT_ORIGIN: https://gitea.newkube.ia86.cc
      FRONTMARK_BASE_PATH: /frontmark
      SVELTIA_ALLOWED_ORIGINS: https://sigmax.dev
      # Optional for confidential OAuth applications:
      # GITEA_CLIENT_SECRET: ${GITEA_CLIENT_SECRET}
    labels:
      - traefik.enable=true
      - traefik.http.routers.frontmark-oauth.rule=Host(`sigmax.dev`) && (PathPrefix(`/frontmark/oauth/forgejo`) || PathPrefix(`/frontmark/api/forgejo/v1`))
      - traefik.http.routers.frontmark-oauth.entrypoints=websecure
      - traefik.http.routers.frontmark-oauth.tls.certresolver=letsencrypt
      - traefik.http.routers.frontmark-oauth.priority=100
      - traefik.http.services.frontmark-oauth.loadbalancer.server.port=4321
    networks:
      - frontmark

volumes:
  traefik-letsencrypt:

networks:
  frontmark:
    name: frontmark

The OAuth/API router has priority 100; the static catch-all has priority 10. That order matters because /frontmark/oauth/forgejo/* and /frontmark/api/forgejo/v1/* must never land on the static Nginx container.

Configure the Forgejo OAuth application with this redirect URI:

https://sigmax.dev/frontmark/admin/

If the OAuth app requires a confidential client secret, keep it only on the proxy container:

GITEA_CLIENT_SECRET=...

The static FrontMark container does not need the secret.

After docker compose up -d, check:

curl -I https://sigmax.dev/frontmark/admin/
curl https://sigmax.dev/frontmark/admin/config.json
curl https://sigmax.dev/frontmark/api/forgejo/v1/version

Expected results:

URLResult
/frontmark/admin/Static admin page.
/frontmark/admin/config.jsonapi_root points to /frontmark/api/forgejo/v1.
/frontmark/api/forgejo/v1/versionJSON response from Forgejo through the proxy.
/frontmark/oauth/forgejo/login/oauth/authorizeRedirects to the configured Forgejo OAuth authorization endpoint.