x-info: github: https://github.com/go-gitea/gitea docs: https://docs.gitea.com/ changelog: https://github.com/go-gitea/gitea/releases homelab_status: stable last_updated: 2026-03-09 version: "3.9" services: server: image: docker.gitea.com/gitea:1.25.1 environment: - TZ=America/New_York - GITEA__database__DB_TYPE=postgres - GITEA__database__HOST=gitea-db:5432 - GITEA__database__NAME=gitea - GITEA__database__PASSWD={{ vault_gitea_db_password }} - GITEA__database__USER=gitea - GITEA__server__ROOT_URL=https://git.castaldifamily.com/ volumes: - /mnt/homelab/apps/gitea/data:/data ports: - "8251:3000" # Keep container labels for compatibility with external route publishers # that inspect task/container metadata instead of Swarm service metadata. labels: - "traefik.enable=true" - "traefik.http.routers.gitea.rule=Host(`git.castaldifamily.com`)" - "traefik.http.routers.gitea.entrypoints=websecure" - "traefik.http.routers.gitea.tls=true" - "traefik.http.routers.gitea.tls.certresolver=cloudflare" - "traefik.http.services.gitea.loadbalancer.server.url=http://{{ edge_routing.swarm.bind_ip }}:8251" networks: - proxy-net deploy: labels: - "traefik.enable=true" - "traefik.http.routers.gitea.rule=Host(`git.castaldifamily.com`)" - "traefik.http.routers.gitea.entrypoints=websecure" - "traefik.http.routers.gitea.tls=true" - "traefik.http.routers.gitea.tls.certresolver=cloudflare" - "traefik.http.services.gitea.loadbalancer.server.url=http://{{ edge_routing.swarm.bind_ip }}:8251" # - "glance.name=Gitea" # - "glance.icon=si:gitea" # - "glance.url=https://git.castaldifamily.com" # - "glance.category=dev-tools" # - "glance.hide=false" replicas: 1 placement: constraints: - node.hostname == {{ gitea_placement_node | default('swarm-manager-1') }} resources: limits: memory: 1G cpus: "0.5" restart_policy: # WHY any (not on-failure): Gitea is a persistent web service. Swarm's # on-failure policy does NOT restart a container that exits cleanly # (SIGTERM → code 0). A graceful shutdown during a rolling update # would leave the service at 0/1 permanently until manually forced. # 'any' ensures the service is always rescheduled regardless of exit code. condition: any delay: 10s update_config: delay: 10s monitor: 30s rollback_config: parallelism: 1 order: stop-first gitea-db: image: postgres:17.4 environment: - TZ=America/New_York - POSTGRES_DB=gitea - POSTGRES_PASSWORD={{ vault_gitea_db_password }} - POSTGRES_USER=gitea volumes: - /mnt/homelab/apps/gitea/data/db:/var/lib/postgresql/data networks: - proxy-net healthcheck: test: ["CMD-SHELL", "pg_isready -U gitea -d gitea"] interval: 10s timeout: 5s retries: 5 start_period: 30s deploy: replicas: 1 placement: constraints: - node.hostname == {{ gitea_placement_node | default('swarm-manager-1') }} resources: limits: memory: 1G cpus: "0.5" restart_policy: condition: on-failure delay: 10s max_attempts: 3 window: 60s update_config: parallelism: 1 order: stop-first failure_action: rollback delay: 10s monitor: 30s rollback_config: parallelism: 1 order: stop-first networks: proxy-net: external: true name: proxy-net # runner: # image: gitea/act_runner:0.2.13 # deploy: # replicas: 1 # placement: # constraints: # - node.hostname == {{ gitea_placement_node | default('swarm-manager-1') }} # resources: # limits: # memory: 512M # cpus: "0.3" # restart_policy: # condition: on-failure # volumes: # - /mnt/homelab/apps/gitea/data/config.yaml:/config.yaml # - /mnt/homelab/apps/gitea/data/runner/data:/data # - /var/run/docker.sock:/var/run/docker.sock # environment: # - TZ=America/New_York # - CONFIG_FILE=/config.yaml # - GITEA_INSTANCE_URL=https://git.castaldifamily.com # - GITEA_RUNNER_REGISTRATION_TOKEN=SET_VAULT_TOKEN_IF_ENABLING_RUNNER # - GITEA_RUNNER_NAME=homelab # networks: # - proxy-net