Petrolyst server infrastructure: Traefik, Trino and Superset

Traefik as the server-wide edge (TLS via Let's Encrypt, dashboard on
traefik.petrolyst.com behind oauth2-proxy and Keycloak). The
fuel-monitoring stack: Superset on atoma.petrolyst.com with Keycloak login
and Petrolyst branding, Trino reading the TimescaleDB fuel-db as catalog
"fuel". Customization lives in petrolyst-insights/, deployment in the other
folders.

Deploys run server-side (deploy/deploy.sh) from a Gitea workflow or
manually; releases are immutable, Superset metadata is backed up before
migrations, and deploy/rollback.sh restores it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nasim SadatandClaude Opus 5.5 committed 2026-10-24 00:33:11 +02:00
1 parent 24327f9462
commit d3800ac9dd
34 files changed
+2117

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
# Edge proxy for the whole server. Deployed first by deploy/deploy.sh, which
# exports TRAEFIK_CONFIG_HASH from the release's ARTIFACTS file.
#
# Any stack joins by attaching a service to the external network "proxy" and
# setting traefik.* labels; apps without their own login can add the "sso"
# middleware (Keycloak client petrolyst, role checked by oauth2-proxy).
name: traefik
services:
socket-proxy:
image: tecnativa/docker-socket-proxy:v0.5.0
environment:
CONTAINERS: "1"
EVENTS: "1"
PING: "1"
VERSION: "1"
POST: "0"
LOG_LEVEL: warning
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket
restart: unless-stopped
mem_limit: 64m
traefik:
image: traefik:v3.7.16
command: ["--configFile=/etc/traefik/static/traefik.yml"]
ports:
- "80:80"
- "443:443"
# Folders, not single files: promotion replaces files by renaming, which a
# single-file bind mount would not follow.
volumes:
- ./static:/etc/traefik/static:ro
- ./dynamic:/etc/traefik/dynamic:ro
- letsencrypt:/letsencrypt
depends_on:
- socket-proxy
networks:
- proxy
- socket
restart: unless-stopped
mem_limit: 256m
healthcheck:
test: ["CMD", "traefik", "healthcheck", "--configFile=/etc/traefik/static/traefik.yml"]
interval: 10s
timeout: 5s
retries: 6
labels:
petrolyst.config-hash: ${TRAEFIK_CONFIG_HASH:?TRAEFIK_CONFIG_HASH unset - run through deploy/deploy.sh}
traefik.enable: "true"
traefik.http.routers.dashboard.rule: Host(`${TRAEFIK_HOST:-traefik.petrolyst.com}`)
traefik.http.routers.dashboard.entrypoints: websecure
traefik.http.routers.dashboard.tls.certresolver: le
traefik.http.routers.dashboard.service: api@internal
traefik.http.routers.dashboard.middlewares: sso,secure-headers@file
# Keycloak gate for the dashboard host only (cookie, return hosts and
# role are tied to it; README "Adding a project"): unauthenticated
# requests are sent to Keycloak, users without petrolyst:traefik-admin
# get 403.
traefik.http.middlewares.sso.forwardauth.address: http://oauth2-proxy:4180/
traefik.http.middlewares.sso.forwardauth.trustForwardHeader: "true"
traefik.http.middlewares.sso.forwardauth.authResponseHeaders: X-Auth-Request-User,X-Auth-Request-Email
oauth2-proxy:
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.5
command:
- --provider=keycloak-oidc
- --oidc-issuer-url=${KEYCLOAK_REALM_URL:-https://iam.thedigitalbridge.io/realms/thedigitalbridge}
- --client-id=petrolyst
- --client-secret-file=/run/secrets/keycloak_petrolyst_client_secret
- --cookie-secret-file=/run/secrets/oauth2_proxy_cookie_secret
- --code-challenge-method=S256
- --allowed-role=petrolyst:traefik-admin
# Required by oauth2-proxy at startup; access is decided by the role above.
- --email-domain=*
- --redirect-url=https://${TRAEFIK_HOST:-traefik.petrolyst.com}/oauth2/callback
- --whitelist-domain=${TRAEFIK_HOST:-traefik.petrolyst.com}
- --cookie-domain=${TRAEFIK_HOST:-traefik.petrolyst.com}
- --cookie-secure=true
- --cookie-samesite=lax
- --reverse-proxy=true
- --upstream=static://202
- --skip-provider-button=true
- --set-xauthrequest=true
- --http-address=0.0.0.0:4180
secrets:
- keycloak_petrolyst_client_secret
- oauth2_proxy_cookie_secret
networks:
- proxy
restart: unless-stopped
mem_limit: 128m
labels:
traefik.enable: "true"
traefik.docker.network: proxy
traefik.http.routers.oauth2.rule: Host(`${TRAEFIK_HOST:-traefik.petrolyst.com}`) && PathPrefix(`/oauth2/`)
traefik.http.routers.oauth2.entrypoints: websecure
traefik.http.routers.oauth2.tls.certresolver: le
traefik.http.routers.oauth2.middlewares: secure-headers@file
traefik.http.services.oauth2.loadbalancer.server.port: "4180"
networks:
proxy:
external: true
socket:
internal: true
volumes:
letsencrypt:
secrets:
keycloak_petrolyst_client_secret:
file: ${PETROLYST_ROOT:-/opt/petrolyst}/secrets/shared/keycloak_petrolyst_client_secret
oauth2_proxy_cookie_secret:
file: ${PETROLYST_ROOT:-/opt/petrolyst}/secrets/traefik/oauth2_proxy_cookie_secret