Files
homelabstack/stacks/dev/docker-compose.yml
T
ginnoirandClaude Opus 4.8 70bc8fd26b fix(dev): stop BookStack inheriting Plane's DATABASE_URL
The dev stack shares one stack.env across all services via env_file, so
BookStack inherited Plane's DATABASE_URL (postgresql://...@postgres_plane).
Laravel prioritizes DATABASE_URL over the individual DB_* vars, so BookStack
connected to Plane's Postgres and 500'd with "could not find driver" (the
LSIO image ships pdo_mysql, not pdo_pgsql). Pin DATABASE_URL to BookStack's
own MariaDB DSN in its environment block.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 20:35:26 -05:00

322 lines
8.7 KiB
YAML

# dev stack — developer tooling: code-server, DBX, BookStack, Plane.
# (Gitea, gitea-runner, postgres_gitea, registry, and registry-ui moved to the
# management plane in portainer-compose.yml.)
#
# Volume -> bind-mount conversions:
# dbx data -> /config/dbx (SSD)
# plane db -> /config/plane/postgres (SSD)
# plane redis -> /config/plane/redis (SSD)
# plane minio -> /storage1/labdata/plane/minio (ZFS; attachments)
services:
# ============================================================ CODE-SERVER
code-server:
container_name: code_server
image: lscr.io/linuxserver/code-server:latest
restart: unless-stopped
networks: [dev, edge]
env_file:
- stack.env
environment:
- PUID=1000
- PGID=1000
- TZ
- DEFAULT_WORKSPACE=/workspace
# Authentik forward_auth at the edge — local password auth disabled via config mount.
volumes:
- /config/code-server:/config
- /config/code-server/code-server.yaml:/config/.config/code-server/config.yaml
- /home/ginnoir/projects:/workspace
- /home/ginnoir/.claude:/home/abc/.claude
- /home/ginnoir/.codex:/home/abc/.codex
ports:
- "8443:8443"
# ============================================================ DBX
dbx:
container_name: dbx
image: t8y2/dbx:latest
restart: unless-stopped
networks: [dev, edge]
volumes:
- /config/dbx:/app/data
ports:
- "4224:4224"
# ============================================================ BOOKSTACK
bookstack:
container_name: bookstack
image: lscr.io/linuxserver/bookstack:latest
restart: unless-stopped
networks: [dev, edge]
env_file:
- stack.env
environment:
- PUID=1000
- PGID=1000
- TZ
- APP_URL=https://docs.ginnoir.com
- APP_KEY
# stack.env leaks Plane's DATABASE_URL; Laravel prioritizes it over DB_* and
# would connect BookStack to postgres_plane. Pin it to BookStack's own MariaDB.
- DATABASE_URL=mysql://bookstack:bookstack_d3v_pass@mariadb_bookstack:3306/bookstack
- DB_HOST=mariadb_bookstack
- DB_PORT=3306
- DB_USER=bookstack
- DB_PASS
- DB_DATABASE=bookstack
- AUTH_METHOD=oidc
- AUTH_AUTO_INITIATE=false
- OIDC_NAME=Authentik
- OIDC_ISSUER=https://auth.ginnoir.com/application/o/bookstack/
- OIDC_ISSUER_DISCOVER=true
- OIDC_END_SESSION_ENDPOINT=true
volumes:
- /config/bookstack:/config
ports:
- "6875:80"
depends_on:
mariadb_bookstack:
condition: service_healthy
mariadb_bookstack:
container_name: mariadb_bookstack
image: lscr.io/linuxserver/mariadb:11.4.8
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
networks: [dev]
env_file:
- stack.env
environment:
- PUID=1000
- PGID=1000
- TZ
- MYSQL_ROOT_PASSWORD
- MYSQL_DATABASE=bookstack
- MYSQL_USER=bookstack
- MYSQL_PASSWORD
volumes:
- /config/bookstack-db:/config
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# ============================================================ PLANE
plane-web:
container_name: plane_web
image: makeplane/plane-frontend:latest
restart: unless-stopped
networks: [dev, edge]
command: node web/server.js
env_file:
- stack.env
environment:
- NEXT_PUBLIC_API_BASE_URL=https://plane.ginnoir.com
- HOSTNAME=0.0.0.0
plane-admin:
container_name: plane_admin
image: makeplane/plane-admin:v0.23.1
restart: unless-stopped
networks: [dev, edge]
command: node admin/server.js
env_file:
- stack.env
environment:
- NEXT_PUBLIC_API_BASE_URL=https://plane.ginnoir.com
- HOSTNAME=0.0.0.0
depends_on:
plane-api:
condition: service_started
plane-web:
condition: service_started
plane-api:
container_name: plane_api
image: makeplane/plane-backend:latest
restart: unless-stopped
networks: [dev, edge]
command: ./bin/docker-entrypoint-api.sh
env_file:
- stack.env
environment:
- SECRET_KEY
- DEBUG=0
- DATABASE_URL
- REDIS_URL=redis://redis_plane:6379/
- CELERY_BROKER_URL=redis://redis_plane:6379/0
- USE_MINIO=1
- AWS_REGION=us-east-1
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_S3_ENDPOINT_URL=http://plane_minio:9000
- AWS_S3_BUCKET_NAME=uploads
- WEB_URL=https://plane.ginnoir.com
- CORS_ALLOWED_ORIGINS=https://plane.ginnoir.com
- GUNICORN_WORKERS=2
depends_on:
postgres_plane:
condition: service_healthy
redis_plane:
condition: service_healthy
plane-migrator:
condition: service_completed_successfully
plane-worker:
container_name: plane_worker
image: makeplane/plane-backend:latest
restart: unless-stopped
networks: [dev]
command: ./bin/docker-entrypoint-worker.sh
env_file:
- stack.env
environment:
- SECRET_KEY
- DEBUG=0
- DATABASE_URL
- REDIS_URL=redis://redis_plane:6379/
- CELERY_BROKER_URL=redis://redis_plane:6379/0
- USE_MINIO=1
- AWS_REGION=us-east-1
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_S3_ENDPOINT_URL=http://plane_minio:9000
- AWS_S3_BUCKET_NAME=uploads
depends_on:
postgres_plane:
condition: service_healthy
redis_plane:
condition: service_healthy
plane-migrator:
condition: service_completed_successfully
plane-beat:
container_name: plane_beat
image: makeplane/plane-backend:latest
restart: unless-stopped
networks: [dev]
command: ./bin/docker-entrypoint-beat.sh
env_file:
- stack.env
environment:
- SECRET_KEY
- DEBUG=0
- DATABASE_URL
- REDIS_URL=redis://redis_plane:6379/
- CELERY_BROKER_URL=redis://redis_plane:6379/0
depends_on:
postgres_plane:
condition: service_healthy
redis_plane:
condition: service_healthy
plane-migrator:
condition: service_completed_successfully
plane-migrator:
container_name: plane_migrator
image: makeplane/plane-backend:latest
restart: "no"
networks: [dev]
command: ./bin/docker-entrypoint-migrator.sh
env_file:
- stack.env
environment:
- SECRET_KEY
- DATABASE_URL
- REDIS_URL=redis://redis_plane:6379/
depends_on:
postgres_plane:
condition: service_healthy
redis_plane:
condition: service_healthy
plane-create-bucket:
container_name: plane_create_bucket
image: minio/mc:latest
restart: "no"
networks: [dev]
depends_on:
plane-minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
until /usr/bin/mc alias set plane_minio http://plane_minio:9000 $$MINIO_USER $$MINIO_PASSWORD; do sleep 1; done;
/usr/bin/mc mb plane_minio/uploads --ignore-existing;
exit 0;
"
env_file:
- stack.env
environment:
- MINIO_USER
- MINIO_PASSWORD
postgres_plane:
container_name: postgres_plane
image: postgres:16-alpine
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
networks: [dev]
env_file:
- stack.env
environment:
- POSTGRES_USER=plane
- POSTGRES_PASSWORD
- POSTGRES_DB=plane
volumes:
- /config/plane/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U plane -d plane"]
interval: 10s
timeout: 5s
retries: 5
redis_plane:
container_name: redis_plane
image: redis:7-alpine
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
networks: [dev]
command: --save 60 1 --loglevel warning
volumes:
- /config/plane/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
plane-minio:
container_name: plane_minio
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
networks: [dev]
command: server /data --console-address ":9001"
env_file:
- stack.env
environment:
- MINIO_ROOT_USER
- MINIO_ROOT_PASSWORD
volumes:
- /storage1/labdata/plane/minio:/data
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:9000/minio/health/live || exit 1"]
interval: 30s
timeout: 20s
retries: 3
start_period: 30s
networks:
dev:
name: dev
driver: bridge
edge:
name: edge
external: true