No description
Find a file
2026-09-26 17:54:42 +00:00
_support-docs Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
dashboards Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
mkdocs Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
notes-backend@829ce6d128 Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
portal Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
travel-app@db9ae32534 Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
tududi Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
vikunja Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
.env.global Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
.gitignore Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00
README.md Initial commit: Freya stacks deployment 2026-09-26 17:54:42 +00:00

🐳 Bazinga Docker Stacks — Deployment Guide

Owner: Michelle · Version: 0.1 · Last Updated: 2026-09-26

This document is a prompt for agents to deploy new Docker service stacks into the Bazinga network and configure external HTTPS access through Traefik.

Repository Info: This repo is automatically synced to Forgejo (ssh://git@ourbazinga.space/bazinga-admin/bbs-stacks.git) at 3:00 AM daily via auto-git-sync.sh.


Quick Start

  1. Choose a stack name — lowercase, hyphen-separated, unique (e.g., my-service)
  2. Create the stack directory — mkdir -p /home/docker/stacks/{stackname}/{config,data}
  3. Copy template files — use the templates below and customize for your service
  4. Register Traefik hostname — add to .env.global and Traefik config
  5. Deploy — run docker compose up -d from the stack directory
  6. Verify — test direct port and HTTPS domain access

Prerequisites

  • Docker and Docker Compose installed on the target machine
  • SSH access to the machine (for Billie-based Traefik routing)
  • Domain name or subdomain under ourbazinga.space (DNS already configured)
  • Network bazinga-net must exist (docker network create bazinga-net if not present)
  • Traefik stack running on Billie (192.168.50.230:443 for HTTPS routing)

Directory Structure

/home/docker/stacks/
├── .env.global                 # Master configuration (all stacks inherit)
├── .env.secrets.master         # Sensitive values (gitignored)
├── server-traefik/             # Traefik router (Billie only)
│   └── config/dynamic/         # Dynamic routing configs (*.yaml files)
├── server-postgres/            # PostgreSQL database (shared)
├── server-redis/               # Redis cache (shared)
│
└── my-service/                 # ← Your new stack
    ├── .env                    # Service-specific env vars
    ├── docker-compose.yml      # Service definition
    ├── config/                 # Config files (mounted as volumes)
    ├── data/                   # Persistent data (DB, files, cache)
    └── [app-specific files]    # mkdocs.yml, nginx.conf, etc.

Step 1: Create Environment File (.env)

Every stack has a .env file that sources global settings and defines service-specific overrides.

# ==============================================================================
# SERVICE: my-service
# Description: [Brief description of what this service does]
# External Port: [PORT] (e.g., 30100)
# Internal Port: [PORT] (what the app listens on inside the container)
# External URL : my-service.ourbazinga.space
# Variables come from .env and .env.global
# ==============================================================================

# Global config (inherited from .env.global, override here if needed)
DOMAIN=ourbazinga.space
NETWORK=bazinga-net
NETWORKS_EXTERNAL=true
TZ=Europe/London
RESTART=unless-stopped

# Resource limits
CPUS=0.50
MEMORY=512M
PUID=1000
PGID=1000
DOCKER_GID=999

# Database access (if using postgres)
POSTGRES_HOST=server-postgres
POSTGRES_PORT=5432
POSTGRES_USER=root
POSTGRES_PASSWORD=root_password

# Redis access (if using cache)
REDIS_HOST=server-redis
REDIS_PORT=6379
REDIS_PASSWORD=""

# Service-specific variables
MY_SERVICE_TRAEFIK_HOST=my-service.ourbazinga.space
MY_SERVICE_EXTERNAL_PORT=30100
MY_SERVICE_INTERNAL_PORT=3000
MY_SERVICE_ADMIN_USER=admin
MY_SERVICE_ADMIN_PASSWORD=generate_secure_password_here
MY_SERVICE_API_KEY=generate_secure_api_key_here

Key Rules:

  • Never commit plaintext passwords to git (.gitignore already excludes .env files)
  • For sensitive values, add to .env.secrets.master and source it: source /home/docker/stacks/.env.secrets.master
  • Use consistent naming: {SERVICE_NAME}_TRAEFIK_HOST, {SERVICE_NAME}_EXTERNAL_PORT, etc.

Step 2: Create docker-compose.yml

Template for a typical Bazinga service:

# ==============================================================================
# SERVICE: my-service
# Description: [Service description]
# External Port: 30100 (exposed to Traefik and host)
# Internal Port: 3000 (app listens on this inside container)
# External URL : my-service.ourbazinga.space (HTTPS via Traefik on Billie)
# Variables come from .env
# ==============================================================================

services:
  my-service:
    image: organization/my-service:latest
    container_name: my-service
    hostname: my-service
    restart: ${RESTART}
    
    # ==========================================================================
    # Traefik Labels
    # For local stacks: Traefik labels auto-route via docker provider
    # For remote stacks: Use file-provider config instead (see Step 4)
    # ==========================================================================
    labels:
      - "traefik.enable=${NETWORKS_EXTERNAL}"
      - "traefik.docker.network=${NETWORK}"
      - "traefik.http.routers.my-service.rule=Host(\`${MY_SERVICE_TRAEFIK_HOST}\`)"
      - "traefik.http.routers.my-service.entrypoints=websecure"
      - "traefik.http.routers.my-service.tls.certresolver=letsencrypt"
      - "traefik.http.services.my-service.loadbalancer.server.port=${MY_SERVICE_INTERNAL_PORT}"
    
    # ==========================================================================
    # Resource Limits
    # ==========================================================================
    deploy:
      resources:
        limits:
          cpus: ${CPUS}
          memory: ${MEMORY}
    
    # ==========================================================================
    # Environment Variables
    # ==========================================================================
    env_file:
      - .env
    environment:
      - TZ=${TZ}
      - PUID=${PUID}
      - PGID=${PGID}
      - DATABASE_HOST=${POSTGRES_HOST}
      - DATABASE_PORT=${POSTGRES_PORT}
      - DATABASE_USER=${POSTGRES_USER}
      - DATABASE_PASSWORD=${POSTGRES_PASSWORD}
      - DATABASE_NAME=my_service_db
      - REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
      - APP_PORT=${MY_SERVICE_INTERNAL_PORT}
      - NODE_ENV=production
    
    # ==========================================================================
    # Volumes (persistent data + config)
    # ==========================================================================
    volumes:
      - ./config:/app/config
      - ./data:/app/data
    
    # ==========================================================================
    # Networking
    # ==========================================================================
    ports:
      - "${MY_SERVICE_EXTERNAL_PORT}:${MY_SERVICE_INTERNAL_PORT}"
    networks:
      - ${NETWORK}

# ==============================================================================
# GLOBAL SECTIONS
# ==============================================================================

networks:
  bazinga-net:
    external: ${NETWORKS_EXTERNAL}

Port Mapping:

  • Format: EXTERNAL_PORT:INTERNAL_PORT
  • EXTERNAL_PORT (left): exposed on the host, routed through Traefik
  • INTERNAL_PORT (right): what the app listens on inside the container
  • Example: 30100:3000 → app listens on 3000, accessible on 30100 from outside

Step 3: Register Traefik Hostname

Add your service to /home/docker/stacks/.env.global:

# In "TRAEFIK HOST REGISTRY" section (around line 24-80):
MY_SERVICE_TRAEFIK_HOST=my-service.ourbazinga.space

# In "PORT ALLOCATION REGISTRY" section (around line 84-138):
MY_SERVICE_EXTERNAL_PORT=30100
MY_SERVICE_INTERNAL_PORT=3000

Step 4: Configure Traefik Routing

Option A: Local Stack (Same machine as Traefik)

If your stack runs on Billie (where Traefik runs), the docker labels automatically configure routing. No additional step needed.

Option B: Remote Stack (Different machine)

If your stack runs on Freya, Mabel, Steve, Percy, or Lara, create a Traefik config file on Billie:

File: /home/docker/stacks/server-traefik/config/dynamic/{machine}-{stackname}.yaml

Example: /home/docker/stacks/server-traefik/config/dynamic/freya-my-service.yaml

# my-service on Freya (192.168.50.245), fronted by Billie's Traefik
# Added 2026-09-26 — [service description]
http:
  routers:
    my-service:
      rule: "Host(`my-service.ourbazinga.space`)"
      entryPoints:
        - websecure
      service: my-service
      tls:
        certResolver: letsencrypt

  services:
    my-service:
      loadBalancer:
        servers:
          - url: "http://192.168.50.245:30100"  # Remote IP + EXTERNAL_PORT

Key points:

  • Router name must be globally unique (use lowercase service name)
  • Service name matches router name
  • URL uses remote machine IP and external port
  • TLS handled by Traefik (automatic Let's Encrypt certs)

Step 5: Database Setup (if needed)

Using Shared PostgreSQL

For services that need PostgreSQL:

# Create the database
ssh 192.168.50.245  # or relevant machine
docker exec server-postgres psql -U root -c "CREATE DATABASE my_service_db;"

In .env:

POSTGRES_HOST=server-postgres
POSTGRES_PORT=5432
POSTGRES_USER=root
POSTGRES_PASSWORD=root_password
DATABASE_NAME=my_service_db

Using Isolated Database

Add to the same docker-compose.yml:

services:
  postgres:
    image: postgres:16-alpine
    container_name: my-service-postgres
    environment:
      POSTGRES_DB: my_service_db
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    networks:
      - ${NETWORK}

Step 6: Deploy

cd /home/docker/stacks/my-service
docker compose up -d
docker compose ps              # Check status
docker compose logs -f         # View logs
docker compose down            # Stop when done

Step 7: Verify

Test Direct Port Access

curl -s -o /dev/null -w "%{http_code}\n" http://192.168.50.245:30100
# Should return 200 (or service-specific code)

Test HTTPS via Traefik

curl -s -o /dev/null -w "%{http_code}\n" https://my-service.ourbazinga.space
# Should return 200

From Inside Container

docker exec my-service curl -s http://localhost:3000/health
# Should return expected response

Troubleshooting

502 Bad Gateway

Cause: Traefik cannot reach the backend.

Check:

  1. Service running: docker ps | grep my-service
  2. Answers on correct port: docker exec my-service curl http://localhost:3000
  3. Port mapping correct: docker compose ps (check PORTS column)
  4. Traefik config correct: cat /home/docker/stacks/server-traefik/config/dynamic/{machine}-{stackname}.yaml
  5. Direct access works: curl http://REMOTE_IP:EXTERNAL_PORT

Connection Refused

Cause: Port not listening or service crashed.

Check:

  1. Container logs: docker logs --tail 50 my-service
  2. Internal port matches app: verify docker-compose.yml port
  3. Resource limits sufficient: increase CPUS or MEMORY
  4. Test image: docker run --rm image-name

Certificate Error

Cause: Let's Encrypt cert not issued or DNS misconfigured.

Check:

  1. Traefik logs: docker logs server-traefik | grep -i cert
  2. DNS resolution: nslookup my-service.ourbazinga.space
  3. Wait for cert issuance (usually within minutes)

Port Already in Use

Cause: Another process using the port.

Check:

  1. See what's using it: lsof -i :30100
  2. Choose different port from registry
  3. Kill if necessary: sudo lsof -ti :30100 | xargs kill -9

Port Allocation Registry

Central ports list in /home/docker/stacks/.env.global (lines 83–138). Choose unused port from range 30000–30799.


Best Practices

  1. Use env vars — never hardcode values
  2. Persistent volumes — always mount data directories
  3. Resource limits — set CPUS and MEMORY
  4. Health checks — add healthcheck section (optional but recommended)
  5. Secrets — use .env.secrets.master for sensitive values
  6. Naming — lowercase, hyphens only (my-service, not MyService)
  7. Documentation — comment docker-compose.yml
  8. Test first — verify direct access before Traefik
  9. Backup volumes — configure via server-duplicati
  10. Monitoring — use docker logs for debugging

Common Patterns

Simple Web App (no DB)

services:
  my-app:
    image: nginx:latest
    ports:
      - "30100:80"
    volumes:
      - ./config:/etc/nginx:ro
    networks:
      - bazinga-net

App + PostgreSQL

services:
  app:
    image: myapp:latest
    ports:
      - "30100:3000"
    environment:
      DATABASE_URL: postgres://user:pass@postgres:5432/mydb
    networks:
      - bazinga-net
    depends_on:
      - postgres

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    networks:
      - bazinga-net

When to Contact Michelle

  • Choosing an external port (check first to avoid conflicts)
  • Adding auth middleware (Authelia, SSO, etc.)
  • Cross-machine setup issues
  • Permission errors on volumes
  • Traefik troubleshooting

  • Traefik: /home/docker/stacks/server-traefik/README.md
  • Global Config: /home/docker/stacks/.env.global
  • Machine IPs: See CLAUDE.md

Version: 0.1 | Updated: 2026-09-26 | Examples: mkdocs, vikunja, tududi