From c68edc70d1414cda767ac300c510f0bbe20a141e Mon Sep 17 00:00:00 2001 From: Leopere Date: Tue, 3 Mar 2026 17:02:49 -0500 Subject: [PATCH] Switch customer stack to Gitea + PostgreSQL two-service pattern - web: Gitea (self-hosted Git), exposed via Traefik behind Authelia - db: PostgreSQL 16, internal backend network only, never exposed - Establishes the canonical web+db template structure for future products Made-with: Cursor --- docker/ss-atlas/templates/stack-template.yml | 53 ++++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/docker/ss-atlas/templates/stack-template.yml b/docker/ss-atlas/templates/stack-template.yml index 5072f0f..aef8dcc 100644 --- a/docker/ss-atlas/templates/stack-template.yml +++ b/docker/ss-atlas/templates/stack-template.yml @@ -1,17 +1,21 @@ # ============================================================================= -# CUSTOMER STACK TEMPLATE — Uptime Kuma +# CUSTOMER STACK TEMPLATE — Gitea + PostgreSQL # ============================================================================= -# This is the Docker Swarm stack that gets deployed for each paying customer. +# This is the Docker Swarm stack deployed for each paying customer. # It defines what product/service they receive when they subscribe. # -# PRODUCT: Uptime Kuma — a self-hosted uptime/monitoring dashboard. +# PRODUCT: Gitea — a self-hosted Git service, backed by PostgreSQL. # Each customer gets their own isolated instance at their subdomain. # -# To sell a different product, replace the `web` service image and adjust -# the port in the Traefik loadbalancer label accordingly. +# Structure: +# web — the application, exposed via Traefik behind Authelia auth +# db — PostgreSQL, internal only (backend network, never exposed) +# +# To sell a different product: replace the `web` image, update the port +# in the Traefik loadbalancer label, and adjust `db` env/image as needed. # # Template variables (injected at deploy time by swarm/client.go): -# {{.ID}} - customer's username (used for unique resource naming) +# {{.ID}} - customer's username (unique resource naming) # {{.Subdomain}} - customer's subdomain (same as ID by default) # {{.Domain}} - base domain (e.g. bc.a250.ca) # {{.TraefikNetwork}} - Traefik overlay network name @@ -21,11 +25,21 @@ # ============================================================================= services: web: - image: louislam/uptime-kuma:1 + image: gitea/gitea:1-rootless + environment: + GITEA__database__DB_TYPE: postgres + GITEA__database__HOST: db:5432 + GITEA__database__NAME: gitea + GITEA__database__USER: gitea + GITEA__database__PASSWD: gitea + GITEA__server__DOMAIN: "{{.Subdomain}}.{{.Domain}}" + GITEA__server__ROOT_URL: "https://{{.Subdomain}}.{{.Domain}}" + GITEA__server__HTTP_PORT: "3000" volumes: - - uptime_data:/app/data + - gitea_data:/var/lib/gitea networks: - traefik_net + - backend deploy: replicas: 1 labels: @@ -35,7 +49,22 @@ services: traefik.http.routers.customer-{{.ID}}-web.entrypoints: "websecure" traefik.http.routers.customer-{{.ID}}-web.tls: "true" traefik.http.routers.customer-{{.ID}}-web.middlewares: "authelia-auth@swarm" - traefik.http.services.customer-{{.ID}}-web.loadbalancer.server.port: "3001" + traefik.http.services.customer-{{.ID}}-web.loadbalancer.server.port: "3000" + restart_policy: + condition: on-failure + + db: + image: postgres:16-alpine + environment: + POSTGRES_DB: gitea + POSTGRES_USER: gitea + POSTGRES_PASSWORD: gitea + volumes: + - db_data:/var/lib/postgresql/data + networks: + - backend + deploy: + replicas: 1 restart_policy: condition: on-failure @@ -43,7 +72,11 @@ networks: traefik_net: external: true name: "atlas_{{.TraefikNetwork}}" + backend: + driver: overlay volumes: - uptime_data: + gitea_data: + driver: local + db_data: driver: local