Phase1_Infrastructure/docker-compose.yml
2026-09-04 11:13:58 +02:00

166 lines
5.6 KiB
YAML

volumes:
postgres_data:
jenkins_home:
gitea_data:
traefik_letsencrypt:
networks:
proxy: # internet-facing network, containers reachable via Traefik
driver: bridge
name: proxy
internal: # no internet access; for DB + backend services only
driver: bridge
internal: true
name: internal
socket-proxy: # isolated network just for Traefik <-> Docker API access
driver: bridge
name: socket-proxy
services:
# ---------------------------------------------------------------------
# Restricts Docker API access so Traefik never touches the raw socket.
# Only exposes the read-only "list containers" endpoint it needs to
# discover services and their labels.
# ---------------------------------------------------------------------
socket-proxy:
image: tecnativa/docker-socket-proxy:0.3
container_name: socket-proxy
restart: unless-stopped
environment:
CONTAINERS: 1
SERVICES: 0
TASKS: 0
NETWORKS: 0
POST: 0
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket-proxy
traefik:
image: traefik:v3.6
container_name: traefik
restart: unless-stopped
command:
- "--api.dashboard=true"
- "--api.insecure=false"
- "--providers.docker=true"
- "--providers.docker.endpoint=tcp://socket-proxy:2375"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.le.acme.email=${ACME_EMAIL}"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge=true"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--log.level=WARN"
ports:
- "80:80"
- "443:443"
volumes:
- traefik_letsencrypt:/letsencrypt
networks:
- proxy
- socket-proxy
labels:
- "traefik.enable=true"
# Global security headers applied to every router below
- "traefik.http.middlewares.sec-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.sec-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.sec-headers.headers.forceSTSHeader=true"
- "traefik.http.middlewares.sec-headers.headers.frameDeny=true"
- "traefik.http.middlewares.sec-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.sec-headers.headers.browserXssFilter=true"
postgres:
image: postgres:16
container_name: postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- internal # no "proxy" network, no published ports -> unreachable from outside
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 30s
timeout: 10s
retries: 5
apache:
image: httpd:2.4
container_name: website
restart: unless-stopped
volumes:
- ./website:/usr/local/apache2/htdocs:ro
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.website.rule=Host(`${DOMAIN}`) || Host(`www.${DOMAIN}`)"
- "traefik.http.routers.website.entrypoints=websecure"
- "traefik.http.routers.website.tls.certresolver=le"
- "traefik.http.routers.website.middlewares=sec-headers@docker"
- "traefik.http.services.website.loadbalancer.server.port=80"
gitea:
image: gitea/gitea:1.22
container_name: gitea
restart: unless-stopped
environment:
USER_UID: 1000
USER_GID: 1000
GITEA__server__ROOT_URL: https://git.${DOMAIN}/
volumes:
- gitea_data:/data
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`git.${DOMAIN}`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls.certresolver=le"
- "traefik.http.routers.gitea.middlewares=sec-headers@docker"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
jenkins:
image: jenkins/jenkins:lts-jdk21
container_name: jenkins
restart: unless-stopped
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock # see README: known risk, needed for CI docker builds
group_add:
- "988" # host docker group GID
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.jenkins.rule=Host(`ci.${DOMAIN}`)"
- "traefik.http.routers.jenkins.entrypoints=websecure"
- "traefik.http.routers.jenkins.tls.certresolver=le"
- "traefik.http.routers.jenkins.middlewares=sec-headers@docker"
- "traefik.http.services.jenkins.loadbalancer.server.port=8080"
api:
build: ./api
container_name: api
restart: unless-stopped
networks:
- proxy
- internal # future: talk to postgres directly, still no internet exposure beyond Traefik
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.api.rule=Host(`api.${DOMAIN}`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=le"
- "traefik.http.routers.api.middlewares=sec-headers@docker"
- "traefik.http.services.api.loadbalancer.server.port=3000"