Files
MISC_DOCKER_COMPOSE_FILES/Open_Project/docker-compose.yml
T
2026-09-13 20:27:51 +01:00

303 lines
12 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ==============================================================================
# OpenProject — Docker Compose (stable/17)
# With SMTP outbound-email support
# ==============================================================================
# Based on:
# https://www.openproject.org/docs/installation-and-operations/installation/docker-compose/
# https://github.com/opf/openproject-docker-compose (branch: stable/17)
# https://www.openproject.org/docs/installation-and-operations/configuration/outbound-emails/
# https://www.openproject.org/docs/installation-and-operations/configuration/environment/
#
# Usage:
# 1. cp .env.example .env # then edit .env with your real values
# 2. sudo mkdir -p /var/openproject/assets
# 3. sudo chown 1000:1000 -R /var/openproject/assets
# 4. docker compose up -d --build --pull always
#
# SMTP variables live in the .env file (see .env.example).
# They are injected into every OpenProject container via the x-op-app anchor.
# ==============================================================================
version: "3.7"
# ---------------------------------------------------------------------------
# Networks frontend faces the proxy; backend is DB / cache only.
# ---------------------------------------------------------------------------
networks:
frontend:
backend:
# ---------------------------------------------------------------------------
# Volumes persisted between restarts / upgrades.
# pgdata PostgreSQL WAL + tables
# opdata uploaded attachments & assets
# ---------------------------------------------------------------------------
volumes:
pgdata:
opdata:
# ===========================================================================
# YAML Anchors shared restart policy, image tag, and environment block.
# ===========================================================================
x-op-restart-policy: &restart_policy
restart: unless-stopped
x-op-image: &image
image: openproject/openproject:${TAG:-17-slim}
# ---------------------------------------------------------------------------
# x-op-app merged into every OpenProject container.
# All SMTP_* variables are pulled from the .env file so that secrets are
# never hard-coded in this file. See .env.example for every placeholder.
# ---------------------------------------------------------------------------
x-op-app: &app
<<: [*image, *restart_policy]
environment:
# --- Core OpenProject ------------------------------------------------
OPENPROJECT_HTTPS: "${OPENPROJECT_HTTPS:-true}"
OPENPROJECT_HOST__NAME: "${OPENPROJECT_HOST__NAME:-localhost:8080}"
OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
OPENPROJECT_EDITION: "${OPENPROJECT_EDITION:-standard}"
# Allow hocuspocus to reach the web container by its service name
OPENPROJECT_ADDITIONAL__HOST__NAMES: "${OPENPROJECT_ADDITIONAL__HOST__NAMES:-web}"
# --- Database ---------------------------------------------------------
DATABASE_URL: "${DATABASE_URL:-postgres://postgres:${POSTGRES_PASSWORD:-p4ssw0rd}@db/openproject?pool=20&encoding=unicode&reconnect=true}"
# --- Cache ------------------------------------------------------------
OPENPROJECT_CACHE__MEMCACHE__SERVER: "cache:11211"
OPENPROJECT_RAILS__CACHE__STORE: "memcache"
# --- Threads ----------------------------------------------------------
RAILS_MIN_THREADS: "${RAILS_MIN_THREADS:-4}"
RAILS_MAX_THREADS: "${RAILS_MAX_THREADS:-16}"
# --- Collaborative editing (Hocuspocus) ------------------------------
OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__URL: "${COLLABORATIVE_SERVER_URL:-wss://${OPENPROJECT_HOST__NAME}/hocuspocus}"
OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__SECRET: "${COLLABORATIVE_SERVER_SECRET:-OVERRIDE_ME_PLEASE}"
# --- Inbound email (IMAP) disabled by default -----------------------
IMAP_ENABLED: "${IMAP_ENABLED:-false}"
# ================================================================
# SMTP Outbound e-mail configuration
# ================================================================
EMAIL_DELIVERY_METHOD: "${EMAIL_DELIVERY_METHOD:-smtp}"
SMTP_ADDRESS: "${SMTP_ADDRESS}"
SMTP_PORT: "${SMTP_PORT:-587}"
SMTP_DOMAIN: "${SMTP_DOMAIN}"
SMTP_AUTHENTICATION: "${SMTP_AUTHENTICATION:-plain}"
SMTP_USER_NAME: "${SMTP_USER_NAME}"
SMTP_PASSWORD: "${SMTP_PASSWORD}"
SMTP_ENABLE_STARTTLS_AUTO: "${SMTP_ENABLE_STARTTLS_AUTO:-true}"
SMTP_SSL: "${SMTP_SSL:-false}"
SMTP_TIMEOUT: "${SMTP_TIMEOUT:-5}"
OPENPROJECT_MAILER__FROM__ADDRESS: "${MAILER_FROM_ADDRESS:-openproject@example.com}"
volumes:
- "${OPDATA:-opdata}:/var/openproject/assets"
# ===========================================================================
# Services
# ===========================================================================
services:
# -----------------------------------------------------------------------
# db PostgreSQL 16
# -----------------------------------------------------------------------
db:
<<: *restart_policy
image: postgres:16-alpine
networks:
- backend
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-p4ssw0rd}"
POSTGRES_DB: openproject
POSTGRES_USER: postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d openproject"]
interval: 10s
timeout: 5s
retries: 5
# -----------------------------------------------------------------------
# cache Memcached
# -----------------------------------------------------------------------
cache:
<<: *restart_policy
image: memcached:alpine
networks:
- backend
# -----------------------------------------------------------------------
# seeder one-shot container
#
# FIX: Must NOT inherit the restart policy from x-op-app. The seeder is a
# one-shot job that exits with code 0 on success. If restart: unless-stopped
# is in effect Docker will keep restarting it and it will appear permanently
# "waiting" to dependent services that expect service_completed_successfully.
#
# We use the *image anchor only (no *restart_policy) and set
# restart: "no" explicitly.
# -----------------------------------------------------------------------
seeder:
<<: *image
restart: "no"
environment:
# Minimal env required for seeder (DB + cache)
OPENPROJECT_HTTPS: "${OPENPROJECT_HTTPS:-true}"
OPENPROJECT_HOST__NAME: "${OPENPROJECT_HOST__NAME:-localhost:8080}"
OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
OPENPROJECT_EDITION: "${OPENPROJECT_EDITION:-standard}"
DATABASE_URL: "${DATABASE_URL:-postgres://postgres:${POSTGRES_PASSWORD:-p4ssw0rd}@db/openproject?pool=20&encoding=unicode&reconnect=true}"
OPENPROJECT_CACHE__MEMCACHE__SERVER: "cache:11211"
OPENPROJECT_RAILS__CACHE__STORE: "memcache"
RAILS_MIN_THREADS: "${RAILS_MIN_THREADS:-4}"
RAILS_MAX_THREADS: "${RAILS_MAX_THREADS:-16}"
volumes:
- "${OPDATA:-opdata}:/var/openproject/assets"
networks:
- backend
command: ["./docker/prod/seeder"]
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
# -----------------------------------------------------------------------
# web Puma application server
#
# FIX: healthcheck timings tightened to match official stable/17 repo
# (interval 10s / timeout 3s / retries 3 / start_period 30s).
# The previous values (30s/5s/5/60s) caused downstream containers that
# depend on service_healthy to wait far too long, making them appear stuck.
# -----------------------------------------------------------------------
web:
<<: *app
networks:
- frontend
- backend
command: ["./docker/prod/web"]
hostname: "${OPENPROJECT_HOST__NAME:-localhost:8080}"
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
seeder:
condition: service_completed_successfully
labels:
- autoheal=true
healthcheck:
test:
- "CMD"
- "curl"
- "-f"
- "http://localhost:8080${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}/health_checks/default"
interval: 10s
timeout: 3s
retries: 3
start_period: 30s
expose:
- "8080"
# -----------------------------------------------------------------------
# autoheal automatically restarts unhealthy containers
# FIX: Added from official stable/17 compose. Without autoheal, an
# unhealthy web container (e.g. stuck in a loop) is never restarted even
# though Docker marks it unhealthy, which can block the seeder indirectly.
# -----------------------------------------------------------------------
autoheal:
image: willfarrell/autoheal:1.2.0
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
environment:
AUTOHEAL_CONTAINER_LABEL: autoheal
AUTOHEAL_START_PERIOD: 600
AUTOHEAL_INTERVAL: 30
# -----------------------------------------------------------------------
# worker Active Job background processor
# -----------------------------------------------------------------------
worker:
<<: *app
networks:
- frontend # needs outbound access for SMTP
- backend
command: ["./docker/prod/worker"]
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
seeder:
condition: service_completed_successfully
# Explicit DNS prevents "Network is unreachable" when the container
# tries to connect to an external SMTP server. See OP#44515.
dns:
- "8.8.8.8"
# -----------------------------------------------------------------------
# cron periodic background tasks (e.g. sending digest emails)
# FIX: Added from official stable/17 compose. Missing cron can prevent
# certain background jobs from running, which can mask seeder-related
# issues or cause incomplete initialisation.
# -----------------------------------------------------------------------
cron:
<<: *app
networks:
- backend
command: ["./docker/prod/cron"]
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
seeder:
condition: service_completed_successfully
# -----------------------------------------------------------------------
# proxy Caddy reverse proxy
# -----------------------------------------------------------------------
proxy:
<<: *restart_policy
image: openproject/openproject:${TAG:-17-slim}
networks:
- frontend
ports:
- "${PORT:-8080}:80"
volumes:
- "${OPDATA:-opdata}:/var/openproject/assets"
command: ["./docker/prod/proxy"]
depends_on:
web:
condition: service_healthy
environment:
OPENPROJECT_HTTPS: "${OPENPROJECT_HTTPS:-true}"
OPENPROJECT_HOST__NAME: "${OPENPROJECT_HOST__NAME:-localhost:8080}"
OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
# -----------------------------------------------------------------------
# hocuspocus WebSocket collaboration server
# -----------------------------------------------------------------------
hocuspocus:
<<: *restart_policy
image: openproject/openproject:${TAG:-17-slim}
networks:
- frontend
- backend
command: ["./docker/prod/hocuspocus"]
depends_on:
web:
condition: service_healthy
environment:
OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__SECRET: "${COLLABORATIVE_SERVER_SECRET:-OVERRIDE_ME_PLEASE}"
expose:
- "3000"