172 lines
6.5 KiB
YAML
172 lines
6.5 KiB
YAML
# Portainer stack: registry-based images (no build). Use with CI/CD webhook redeploy.
|
|
# Set in Portainer stack env (or .env): REGISTRY, IMAGE_TAG (defaults below).
|
|
# Images: ${REGISTRY}/mifi-holdings/shorty-qr-api:${IMAGE_TAG}, shorty-qr-web:${IMAGE_TAG}
|
|
|
|
services:
|
|
kutt_db:
|
|
image: postgres:16-alpine
|
|
container_name: mifi-shorty_kutt_db
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
volumes:
|
|
- /mnt/config/docker/kutt/postgres:/var/lib/postgresql/data
|
|
environment:
|
|
POSTGRES_USER: ${DB_USER:-kutt}
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD}
|
|
POSTGRES_DB: ${DB_NAME:-kutt}
|
|
healthcheck:
|
|
test:
|
|
[
|
|
'CMD-SHELL',
|
|
'pg_isready -U ${DB_USER:-kutt} -d ${DB_NAME:-kutt}',
|
|
]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
kutt_redis:
|
|
image: redis:8-alpine
|
|
container_name: mifi-shorty_kutt_redis
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
volumes:
|
|
- /mnt/config/docker/kutt/redis:/data
|
|
command: redis-server --appendonly yes
|
|
healthcheck:
|
|
test: ['CMD', 'redis-cli', 'ping']
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
kutt:
|
|
image: kutt/kutt:latest
|
|
container_name: mifi-shorty_kutt
|
|
restart: unless-stopped
|
|
networks:
|
|
- marina-net
|
|
- backend
|
|
depends_on:
|
|
kutt_db:
|
|
condition: service_healthy
|
|
kutt_redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test:
|
|
- CMD
|
|
- node
|
|
- -e
|
|
- 'require("http").get("http://127.0.0.1:3000/", (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on("error", () => process.exit(1))'
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
environment:
|
|
ADMIN_EMAILS: ${ADMIN_EMAILS:?Set ADMIN_EMAILS}
|
|
DEFAULT_DOMAIN: mifi.me
|
|
DB_CLIENT: pg
|
|
DB_HOST: kutt_db
|
|
DB_PORT: '5432'
|
|
DB_USER: ${DB_USER:-kutt}
|
|
DB_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD}
|
|
DB_NAME: ${DB_NAME:-kutt}
|
|
DISALLOW_ANONYMOUS_LINKS: 'true'
|
|
DISALLOW_REGISTRATION: 'true'
|
|
LINK_LENGTH: 6
|
|
JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET}
|
|
MAIL_ENABLED: 'true'
|
|
MAIL_HOST: 'mail.mifi.holdings'
|
|
MAIL_PORT: '465'
|
|
MAIL_SECURE: 'true'
|
|
MAIL_USER: 'mailbot@mifi.ventures'
|
|
MAIL_FROM: 'mifi Holdings Shorty <noreply@mifi.holdings>'
|
|
MAIL_PASSWORD: '${SMTP_PASSWORD:?Set SMTP_PASSWORD}'
|
|
NODE_ENV: production
|
|
# OIDC_ENABLED: 'true'
|
|
# OIDC_ISSUER: 'https://git.mifi.dev'
|
|
# OIDC_CLIENT_ID: '2a6d1ecd-4e2f-42e5-922e-22e878230488'
|
|
# OIDC_CLIENT_SECRET: '${OIDC_CLIENT_SECRET:?Set OIDC_CLIENT_SECRET}'
|
|
REDIS_ENABLED: 'true'
|
|
REDIS_HOST: kutt_redis
|
|
REDIS_PORT: '6379'
|
|
SITE_DOMAIN: link.mifi.me
|
|
SITE_NAME: 'mifi Shorty'
|
|
labels:
|
|
- 'traefik.enable=true'
|
|
- 'docker.network=marina-net'
|
|
- 'traefik.http.routers.kutt-mifi.rule=Host(`mifi.me`)'
|
|
- 'traefik.http.routers.kutt-mifi.entrypoints=websecure'
|
|
- 'traefik.http.routers.kutt-mifi.tls.certresolver=letsencrypt'
|
|
- 'traefik.http.routers.kutt-mifi.service=kutt-short'
|
|
- 'traefik.http.services.kutt-short.loadbalancer.server.port=3000'
|
|
# Backend timeout: use transport from file provider (see traefik-kutt-timeout.example.yml).
|
|
- 'traefik.http.services.kutt-short.loadbalancer.serversTransport=kutt-long-timeout@file'
|
|
|
|
qr_api:
|
|
image: ${REGISTRY:-git.mifi.dev}/mifi-holdings/shorty-qr-api:${IMAGE_TAG:-latest}
|
|
container_name: mifi-shorty_qr_api
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
volumes:
|
|
- /mnt/config/docker/qr/db:/data
|
|
- /mnt/config/docker/qr/uploads:/uploads
|
|
environment:
|
|
PORT: '8080'
|
|
DB_PATH: /data/db.sqlite
|
|
UPLOADS_PATH: /uploads
|
|
KUTT_API_KEY: ${KUTT_API_KEY:-}
|
|
KUTT_BASE_URL: http://kutt:3000
|
|
SHORT_DOMAIN: https://mifi.me
|
|
healthcheck:
|
|
test:
|
|
- CMD
|
|
- node
|
|
- -e
|
|
- 'require("http").get("http://127.0.0.1:8080/health", (r) => { r.resume(); process.exit(r.statusCode === 200 ? 0 : 1); }).on("error", () => process.exit(1))'
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
|
|
qr_web:
|
|
image: ${REGISTRY:-git.mifi.dev}/mifi-holdings/shorty-qr-web:${IMAGE_TAG:-latest}
|
|
container_name: mifi-shorty_qr_web
|
|
restart: unless-stopped
|
|
networks:
|
|
- marina-net
|
|
- backend
|
|
# Use service_started so the stack can deploy even if qr_api is still broken; switch to service_healthy once qr_api image is fixed.
|
|
depends_on:
|
|
qr_api:
|
|
condition: service_started
|
|
environment:
|
|
QR_API_URL: http://qr_api:8080
|
|
healthcheck:
|
|
test:
|
|
- CMD
|
|
- node
|
|
- -e
|
|
- 'require("http").get("http://0.0.0.0:3000/", {timeout: 5000}, (r) => { r.resume(); process.exit(r.statusCode >= 200 && r.statusCode < 500 ? 0 : 1); }).on("error", () => process.exit(1))'
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 15s
|
|
labels:
|
|
- 'traefik.enable=true'
|
|
- 'docker.network=marina-net'
|
|
- 'traefik.http.routers.qr-web.rule=Host(`qr.mifi.dev`)'
|
|
- 'traefik.http.routers.qr-web.entrypoints=websecure'
|
|
- 'traefik.http.routers.qr-web.tls.certresolver=letsencrypt'
|
|
- 'traefik.http.routers.qr-web.service=qr-web'
|
|
- 'traefik.http.routers.qr-web.middlewares=qr-web-basicauth'
|
|
- 'traefik.http.middlewares.qr-web-basicauth.basicauth.users=mifi:$$apr1$$9fgAWvE1$$bLGgUtpFjdaexkV5gooWq.'
|
|
- 'traefik.http.services.qr-web.loadbalancer.server.port=3000'
|
|
|
|
networks:
|
|
marina-net:
|
|
external: true
|
|
backend:
|
|
driver: bridge
|