# 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:/workspace 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 - 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