Files
indee-demo/docker-compose.yml
Dorian eeffce4baa Add versioned image tags to force Portainer to rebuild images
Docker was reusing old cached images even on redeploy. By adding
explicit image names with version tags (e.g. indeehub-app:v2),
Docker must build new images since the old ones had no tag or a
different tag. Bump the version (v2 -> v3) to force future rebuilds.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 18:11:51 +00:00

233 lines
8.0 KiB
YAML

# ═══════════════════════════════════════════════════════════════
# IndeeHub — Production Stack for Portainer
# ═══════════════════════════════════════════════════════════════
#
# All ${VARIABLES} are resolved by Portainer at deploy time.
# Configure them in Portainer → Stacks → Environment variables
# before deploying.
#
# See env.portainer for the full list of required variables.
#
# For local development, use: docker compose -f docker-compose.dev.yml up
# ═══════════════════════════════════════════════════════════════
version: '3.8'
services:
# ── Frontend (nginx serving built Vue app) ──────────────────
app:
image: indeehub-app:v2
build:
context: .
dockerfile: Dockerfile
args:
CACHEBUST: "2"
VITE_USE_MOCK_DATA: "false"
VITE_CONTENT_ORIGIN: ${FRONTEND_URL}
VITE_INDEEHUB_API_URL: /api
VITE_INDEEHUB_CDN_URL: /storage
VITE_NOSTR_RELAYS: ""
restart: unless-stopped
ports:
- "${APP_PORT:-7777}:7777"
depends_on:
- relay
- api
networks:
- indeedhub-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7777/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ── Backend API (NestJS) ────────────────────────────────────
api:
image: indeehub-api:v2
build:
context: ./backend
dockerfile: Dockerfile
args:
CACHEBUST: "2"
restart: unless-stopped
environment:
# ── Core ─────────────────────────────────────────────
ENVIRONMENT: production
PORT: 4000
DOMAIN: ${DOMAIN}
FRONTEND_URL: ${FRONTEND_URL}
# ── Database ─────────────────────────────────────────
DATABASE_HOST: postgres
DATABASE_PORT: 5432
DATABASE_USER: ${POSTGRES_USER}
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_NAME: ${POSTGRES_DB}
# ── Redis / BullMQ ───────────────────────────────────
QUEUE_HOST: redis
QUEUE_PORT: 6379
QUEUE_PASSWORD: ${REDIS_PASSWORD:-}
# ── S3 / MinIO ──────────────────────────────────────
S3_ENDPOINT: http://minio:9000
AWS_REGION: us-east-1
AWS_ACCESS_KEY: ${S3_ACCESS_KEY}
AWS_SECRET_KEY: ${S3_SECRET_KEY}
S3_PRIVATE_BUCKET_NAME: indeedhub-private
S3_PUBLIC_BUCKET_NAME: indeedhub-public
S3_PUBLIC_BUCKET_URL: ${S3_PUBLIC_BUCKET_URL}
# ── BTCPay Server ───────────────────────────────────
BTCPAY_URL: ${BTCPAY_URL}
BTCPAY_API_KEY: ${BTCPAY_API_KEY}
BTCPAY_STORE_ID: ${BTCPAY_STORE_ID}
BTCPAY_WEBHOOK_SECRET: ${BTCPAY_WEBHOOK_SECRET}
# ── Nostr Auth / JWT ─────────────────────────────────
NOSTR_JWT_SECRET: ${NOSTR_JWT_SECRET}
NOSTR_JWT_EXPIRES_IN: ${NOSTR_JWT_EXPIRES_IN:-7d}
# ── AES-128 Content Encryption ──────────────────────
AES_MASTER_SECRET: ${AES_MASTER_SECRET}
# ── Admin API ────────────────────────────────────────
ADMIN_API_KEY: ${ADMIN_API_KEY:-}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
minio:
condition: service_started
networks:
- indeedhub-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4000/nostr-auth/health"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
# ── PostgreSQL Database ─────────────────────────────────────
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- indeedhub-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U indeedhub"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# ── Redis (BullMQ job queue) ────────────────────────────────
redis:
image: redis:7-alpine
restart: unless-stopped
command: >
sh -c "if [ -n '${REDIS_PASSWORD:-}' ]; then
redis-server --requirepass '${REDIS_PASSWORD}' --appendonly yes;
else
redis-server --appendonly yes;
fi"
volumes:
- redis-data:/data
networks:
- indeedhub-network
# ── MinIO (S3-compatible object storage) ────────────────────
minio:
image: minio/minio:latest
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
volumes:
- minio-data:/data
networks:
- indeedhub-network
# ── MinIO bucket init (one-shot: creates required buckets) ──
minio-init:
image: minio/mc:latest
depends_on:
- minio
entrypoint: >
/bin/sh -c "
sleep 5;
mc alias set local http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
mc mb local/indeedhub-private --ignore-existing;
mc mb local/indeedhub-public --ignore-existing;
mc anonymous set download local/indeedhub-public;
echo 'MinIO buckets initialized';
"
networks:
- indeedhub-network
restart: "no"
# ── FFmpeg Transcoding Worker ───────────────────────────────
ffmpeg-worker:
image: indeehub-ffmpeg:v2
build:
context: ./backend
dockerfile: Dockerfile.ffmpeg
args:
CACHEBUST: "2"
restart: unless-stopped
environment:
ENVIRONMENT: production
DATABASE_HOST: postgres
DATABASE_PORT: 5432
DATABASE_USER: ${POSTGRES_USER}
DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_NAME: ${POSTGRES_DB}
QUEUE_HOST: redis
QUEUE_PORT: 6379
QUEUE_PASSWORD: ${REDIS_PASSWORD:-}
S3_ENDPOINT: http://minio:9000
AWS_REGION: us-east-1
AWS_ACCESS_KEY: ${S3_ACCESS_KEY}
AWS_SECRET_KEY: ${S3_SECRET_KEY}
S3_PRIVATE_BUCKET_NAME: indeedhub-private
S3_PUBLIC_BUCKET_NAME: indeedhub-public
S3_PUBLIC_BUCKET_URL: ${S3_PUBLIC_BUCKET_URL}
AES_MASTER_SECRET: ${AES_MASTER_SECRET}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
minio:
condition: service_started
networks:
- indeedhub-network
# ── Nostr Relay ─────────────────────────────────────────────
relay:
image: scsibug/nostr-rs-relay:latest
restart: unless-stopped
volumes:
- relay-data:/usr/src/app/db
networks:
- indeedhub-network
networks:
indeedhub-network:
driver: bridge
volumes:
postgres-data:
redis-data:
minio-data:
relay-data: