Restructure docker into layered compose files, add a Makefile
Move compose.yml/compose.override.yml/compose.traefik.yml into docker/ and
split into the explicit prod -> dev -> local layering; compose.override.yml
had to be renamed because docker compose auto-loads that filename, which
defeats the layering.
- external network traefik-public -> proxy (shared with the website stack)
- frontend host dashboard.${DOMAIN} -> app.${DOMAIN}
- stable container_names, security_opt no-new-privileges on prod services
- adminer bound to 127.0.0.1 in dev instead of all interfaces
- .env.example replaces the committed .env
- pre-commit biome hook ran npm in a bun repo
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1916f7f778
commit
529b5f9ed5
@@ -21,7 +21,7 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: local-biome-check
|
- id: local-biome-check
|
||||||
name: biome check
|
name: biome check
|
||||||
entry: npm run lint
|
entry: bun run lint
|
||||||
language: system
|
language: system
|
||||||
types: [text]
|
types: [text]
|
||||||
files: ^frontend/
|
files: ^frontend/
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# ─── Fluksio app Makefile ───
|
||||||
|
# Convenience targets for development, testing, linting, and deployment.
|
||||||
|
# The workspace root delegates to these (see ../Makefile).
|
||||||
|
|
||||||
|
.PHONY: dev-utils dev dev-local up down update install dev-backend dev-frontend \
|
||||||
|
generate-client test test-backend test-frontend lint lint-backend \
|
||||||
|
lint-frontend clean help
|
||||||
|
|
||||||
|
COMPOSE_ROOT := $(CURDIR)
|
||||||
|
# Explicit project name keeps this stack isolated from the sibling website
|
||||||
|
# stack (otherwise both default to "docker", the directory their compose
|
||||||
|
# files live in).
|
||||||
|
COMPOSE_PROJECT := fluksio-app
|
||||||
|
# Compose interpolation needs the project-local .env before reading compose.yml.
|
||||||
|
COMPOSE := docker compose -p $(COMPOSE_PROJECT) --env-file $(COMPOSE_ROOT)/.env
|
||||||
|
COMPOSE_PROD := $(COMPOSE) -f docker/compose.yml
|
||||||
|
COMPOSE_DEV := $(COMPOSE_PROD) -f docker/compose.dev.yml
|
||||||
|
# Integrated local stack: dev stack wired onto the shared `proxy` network.
|
||||||
|
COMPOSE_LOCAL := $(COMPOSE_DEV) -f docker/compose.local.yml
|
||||||
|
|
||||||
|
help: ## Show available targets
|
||||||
|
@awk 'BEGIN{FS=":.*?## "} /^[a-zA-Z_-]+:.*?##/ {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
|
# ── Development (Docker) ──────────────────────────────────────────
|
||||||
|
|
||||||
|
dev-utils: ## Start only the utility containers (db, adminer, proxy, mailcatcher, prestart)
|
||||||
|
$(COMPOSE_DEV) up --build db adminer proxy mailcatcher prestart
|
||||||
|
|
||||||
|
dev: ## Start the full dev stack (includes a local Traefik proxy)
|
||||||
|
$(COMPOSE_DEV) up --build
|
||||||
|
|
||||||
|
dev-local: ## Start the integrated local stack (called by the root `make dev`)
|
||||||
|
DOMAIN=$${DOMAIN:-localhost} ENVIRONMENT=$${ENVIRONMENT:-local} \
|
||||||
|
$(COMPOSE_LOCAL) up --build -d proxy db adminer prestart backend frontend mailcatcher
|
||||||
|
|
||||||
|
up: ## Start the production stack
|
||||||
|
$(COMPOSE_PROD) up --build -d
|
||||||
|
|
||||||
|
update: ## Pull, rebuild using the layer cache, and recreate changed containers
|
||||||
|
git pull
|
||||||
|
$(COMPOSE_PROD) build
|
||||||
|
$(COMPOSE_PROD) up -d --remove-orphans
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
down: ## Stop all running containers
|
||||||
|
-$(COMPOSE_LOCAL) down
|
||||||
|
-$(COMPOSE_PROD) down
|
||||||
|
|
||||||
|
# ── Development (local, no Docker) ───────────────────────────────
|
||||||
|
# Run `make dev-backend` and `make dev-frontend` in two separate terminals.
|
||||||
|
|
||||||
|
install: ## Install all dependencies (backend + frontend)
|
||||||
|
cd backend && uv sync
|
||||||
|
cd frontend && bun install
|
||||||
|
|
||||||
|
dev-backend: ## Start the FastAPI backend with hot-reload (local)
|
||||||
|
cd backend && uv run fastapi dev app/main.py
|
||||||
|
|
||||||
|
dev-frontend: ## Start the Vite dev server (local)
|
||||||
|
cd frontend && bun dev
|
||||||
|
|
||||||
|
generate-client: ## Regenerate the frontend SDK from the backend's OpenAPI schema
|
||||||
|
bash scripts/generate-client.sh
|
||||||
|
|
||||||
|
# ── Testing ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
test: test-backend test-frontend ## Run all tests (backend + frontend)
|
||||||
|
|
||||||
|
test-backend: ## Run backend tests (pytest + coverage)
|
||||||
|
cd backend && uv run bash scripts/tests-start.sh
|
||||||
|
|
||||||
|
test-frontend: ## Run frontend tests (Playwright e2e)
|
||||||
|
cd frontend && bunx playwright test
|
||||||
|
|
||||||
|
# ── Linting ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
lint: lint-backend lint-frontend ## Run all linters
|
||||||
|
|
||||||
|
lint-backend: ## Lint backend with ruff + mypy
|
||||||
|
cd backend && uv run ruff check .
|
||||||
|
cd backend && uv run ruff format --check .
|
||||||
|
cd backend && uv run mypy app
|
||||||
|
|
||||||
|
lint-frontend: ## Lint frontend with biome
|
||||||
|
cd frontend && bun run lint
|
||||||
|
|
||||||
|
# ── Cleanup ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
clean: ## Remove build artifacts and caches
|
||||||
|
rm -rf frontend/dist frontend/blob-report frontend/test-results
|
||||||
|
rm -rf backend/.pytest_cache backend/htmlcov
|
||||||
|
find backend -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
|
# Local development overrides: a self-contained stack with its own Traefik,
|
||||||
|
# published host ports, hot reload and the mail/browser test helpers.
|
||||||
|
# Never used in production — the Makefile only layers it for `make dev*`.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
# Local services are available on their ports, but also available on:
|
# Local Traefik. Services stay reachable both on their published ports and
|
||||||
# http://api.localhost.tiangolo.com: backend
|
# on http://api.localhost / http://app.localhost via the routers in
|
||||||
# http://dashboard.localhost.tiangolo.com: frontend
|
# compose.yml. Dashboard at http://localhost:8090.
|
||||||
# etc. To enable it, update .env, set:
|
|
||||||
# DOMAIN=localhost.tiangolo.com
|
|
||||||
proxy:
|
proxy:
|
||||||
image: traefik:3.6
|
image: traefik:3.6
|
||||||
volumes:
|
volumes:
|
||||||
@@ -12,37 +14,25 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "8090:8080"
|
- "8090:8080"
|
||||||
# Duplicate the command from compose.yml to add --api.insecure=true
|
|
||||||
command:
|
command:
|
||||||
# Enable Docker in Traefik, so that it reads labels from Docker services
|
|
||||||
- --providers.docker
|
- --providers.docker
|
||||||
# Add a constraint to only use services with the label for this stack
|
- --providers.docker.constraints=Label(`traefik.constraint-label`, `proxy`)
|
||||||
- --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`)
|
|
||||||
# Do not expose all Docker services, only the ones explicitly exposed
|
|
||||||
- --providers.docker.exposedbydefault=false
|
- --providers.docker.exposedbydefault=false
|
||||||
# Create an entrypoint "http" listening on port 80
|
|
||||||
- --entrypoints.http.address=:80
|
- --entrypoints.http.address=:80
|
||||||
# Create an entrypoint "https" listening on port 443
|
|
||||||
- --entrypoints.https.address=:443
|
- --entrypoints.https.address=:443
|
||||||
# Enable the access log, with HTTP requests
|
|
||||||
- --accesslog
|
- --accesslog
|
||||||
# Enable the Traefik log, for configurations and errors
|
|
||||||
- --log
|
- --log
|
||||||
# Enable debug logging for local development
|
|
||||||
- --log.level=DEBUG
|
- --log.level=DEBUG
|
||||||
# Enable the Dashboard and API
|
|
||||||
- --api
|
- --api
|
||||||
# Enable the Dashboard and API in insecure mode for local development
|
|
||||||
- --api.insecure=true
|
- --api.insecure=true
|
||||||
labels:
|
labels:
|
||||||
# Enable Traefik for this service, to make it available in the public network
|
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.constraint-label=traefik-public
|
- traefik.constraint-label=proxy
|
||||||
# Dummy https-redirect middleware that doesn't really redirect, only to
|
# Dummy https-redirect middleware that doesn't really redirect, so the
|
||||||
# allow running it locally
|
# production routers' middleware reference still resolves locally.
|
||||||
- traefik.http.middlewares.https-redirect.contenttype.autodetect=false
|
- traefik.http.middlewares.https-redirect.contenttype.autodetect=false
|
||||||
networks:
|
networks:
|
||||||
- traefik-public
|
- proxy
|
||||||
- default
|
- default
|
||||||
|
|
||||||
db:
|
db:
|
||||||
@@ -53,16 +43,15 @@ services:
|
|||||||
adminer:
|
adminer:
|
||||||
restart: "no"
|
restart: "no"
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "127.0.0.1:8080:8080"
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
restart: "no"
|
restart: "no"
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: backend/Dockerfile
|
dockerfile: backend/Dockerfile
|
||||||
# command: sleep infinity # Infinite loop to keep container alive doing nothing
|
|
||||||
command:
|
command:
|
||||||
- fastapi
|
- fastapi
|
||||||
- run
|
- run
|
||||||
@@ -70,22 +59,21 @@ services:
|
|||||||
- "app/main.py"
|
- "app/main.py"
|
||||||
develop:
|
develop:
|
||||||
watch:
|
watch:
|
||||||
- path: ./backend
|
- path: ../backend
|
||||||
action: sync
|
action: sync
|
||||||
target: /app/backend
|
target: /app/backend
|
||||||
ignore:
|
ignore:
|
||||||
- ./backend/.venv
|
- ../backend/.venv
|
||||||
- .venv
|
- .venv
|
||||||
- path: ./backend/pyproject.toml
|
- path: ../backend/pyproject.toml
|
||||||
action: rebuild
|
action: rebuild
|
||||||
# TODO: remove once coverage is done locally
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./backend/htmlcov:/app/backend/htmlcov
|
- ../backend/htmlcov:/app/backend/htmlcov
|
||||||
environment:
|
environment:
|
||||||
SMTP_HOST: "mailcatcher"
|
SMTP_HOST: "mailcatcher"
|
||||||
SMTP_PORT: "1025"
|
SMTP_PORT: "1025"
|
||||||
SMTP_TLS: "false"
|
SMTP_TLS: "false"
|
||||||
EMAILS_FROM_EMAIL: "noreply@example.com"
|
EMAILS_FROM_EMAIL: "noreply@fluksio.com"
|
||||||
|
|
||||||
mailcatcher:
|
mailcatcher:
|
||||||
image: schickling/mailcatcher
|
image: schickling/mailcatcher
|
||||||
@@ -98,7 +86,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5173:80"
|
- "5173:80"
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: frontend/Dockerfile
|
dockerfile: frontend/Dockerfile
|
||||||
args:
|
args:
|
||||||
- VITE_API_URL=http://localhost:8000
|
- VITE_API_URL=http://localhost:8000
|
||||||
@@ -106,7 +94,7 @@ services:
|
|||||||
|
|
||||||
playwright:
|
playwright:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: frontend/Dockerfile.playwright
|
dockerfile: frontend/Dockerfile.playwright
|
||||||
args:
|
args:
|
||||||
- VITE_API_URL=http://backend:8000
|
- VITE_API_URL=http://backend:8000
|
||||||
@@ -116,20 +104,20 @@ services:
|
|||||||
- backend
|
- backend
|
||||||
- mailcatcher
|
- mailcatcher
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- ../.env
|
||||||
environment:
|
environment:
|
||||||
- VITE_API_URL=http://backend:8000
|
- VITE_API_URL=http://backend:8000
|
||||||
- MAILCATCHER_HOST=http://mailcatcher:1080
|
- MAILCATCHER_HOST=http://mailcatcher:1080
|
||||||
# For the reports when run locally
|
|
||||||
- PLAYWRIGHT_HTML_HOST=0.0.0.0
|
- PLAYWRIGHT_HTML_HOST=0.0.0.0
|
||||||
- CI=${CI}
|
- CI=${CI}
|
||||||
volumes:
|
volumes:
|
||||||
- ./frontend/blob-report:/app/frontend/blob-report
|
- ../frontend/blob-report:/app/frontend/blob-report
|
||||||
- ./frontend/test-results:/app/frontend/test-results
|
- ../frontend/test-results:/app/frontend/test-results
|
||||||
ports:
|
ports:
|
||||||
- 9323:9323
|
- 9323:9323
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
traefik-public:
|
# Self-contained dev: create a stack-local network instead of expecting the
|
||||||
# For local dev, don't expect an external Traefik network
|
# shared one. compose.local.yml flips this back for the integrated stack.
|
||||||
|
proxy:
|
||||||
external: false
|
external: false
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Integrated local stack — layered on top of compose.yml + compose.dev.yml by
|
||||||
|
# the root `make dev`. Reattaches this stack's Traefik to the shared external
|
||||||
|
# `proxy` network so it also routes the website stack's containers, and serves
|
||||||
|
# everything over plain http on *.${DOMAIN} (default *.localhost).
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
proxy:
|
||||||
|
networks:
|
||||||
|
- proxy
|
||||||
|
- default
|
||||||
|
|
||||||
|
backend:
|
||||||
|
environment:
|
||||||
|
- ENVIRONMENT=local
|
||||||
|
- FRONTEND_HOST=http://app.${DOMAIN:-localhost}
|
||||||
|
- BACKEND_CORS_ORIGINS=http://${DOMAIN:-localhost},http://app.${DOMAIN:-localhost}
|
||||||
|
|
||||||
|
prestart:
|
||||||
|
environment:
|
||||||
|
- ENVIRONMENT=local
|
||||||
|
- FRONTEND_HOST=http://app.${DOMAIN:-localhost}
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
args:
|
||||||
|
- VITE_API_URL=http://api.${DOMAIN:-localhost}
|
||||||
|
- NODE_ENV=development
|
||||||
|
|
||||||
|
networks:
|
||||||
|
# Shared with the website stack; created by the root scripts/setup.sh.
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
@@ -10,8 +10,8 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
# Enable Traefik for this service, to make it available in the public network
|
# Enable Traefik for this service, to make it available in the public network
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
# Use the traefik-public network (declared below)
|
# Use the proxy network (declared below)
|
||||||
- traefik.docker.network=traefik-public
|
- traefik.docker.network=proxy
|
||||||
# Define the port inside of the Docker service to use
|
# Define the port inside of the Docker service to use
|
||||||
- traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080
|
- traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080
|
||||||
# Make Traefik use this domain (from an environment variable) in HTTP
|
# Make Traefik use this domain (from an environment variable) in HTTP
|
||||||
@@ -39,7 +39,7 @@ services:
|
|||||||
# Add Docker as a mounted volume, so that Traefik can read the labels of other services
|
# Add Docker as a mounted volume, so that Traefik can read the labels of other services
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
# Mount the volume to store the certificates
|
# Mount the volume to store the certificates
|
||||||
- traefik-public-certificates:/certificates
|
- traefik-certificates:/certificates
|
||||||
command:
|
command:
|
||||||
# Enable Docker in Traefik, so that it reads labels from Docker services
|
# Enable Docker in Traefik, so that it reads labels from Docker services
|
||||||
- --providers.docker
|
- --providers.docker
|
||||||
@@ -64,14 +64,14 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
# Use the public network created to be shared between Traefik and
|
# Use the public network created to be shared between Traefik and
|
||||||
# any other service that needs to be publicly available with HTTPS
|
# any other service that needs to be publicly available with HTTPS
|
||||||
- traefik-public
|
- proxy
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
# Create a volume to store the certificates, even if the container is recreated
|
# Create a volume to store the certificates, even if the container is recreated
|
||||||
traefik-public-certificates:
|
traefik-certificates:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
# Use the previously created public network "traefik-public", shared with other
|
# Use the previously created public network "proxy", shared with other
|
||||||
# services that need to be publicly available via this Traefik
|
# services that need to be publicly available via this Traefik
|
||||||
traefik-public:
|
proxy:
|
||||||
external: true
|
external: true
|
||||||
@@ -1,8 +1,18 @@
|
|||||||
|
# Production stack. Layered by the Makefile:
|
||||||
|
# compose.yml → production (Traefik + TLS, restart always)
|
||||||
|
# + compose.dev.yml → local dev (published ports, hot reload)
|
||||||
|
# + compose.local.yml → integrated stack on the shared `proxy` net
|
||||||
|
# Paths are relative to this directory, which compose uses as the project
|
||||||
|
# directory (build contexts therefore point at `..`, the repo root).
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: postgres:18
|
image: postgres:18
|
||||||
|
container_name: fluksio-db
|
||||||
restart: always
|
restart: always
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
@@ -12,7 +22,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- app-db-data:/var/lib/postgresql/data/pgdata
|
- app-db-data:/var/lib/postgresql/data/pgdata
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- ../.env
|
||||||
environment:
|
environment:
|
||||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set}
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD?Variable not set}
|
||||||
@@ -21,34 +31,29 @@ services:
|
|||||||
|
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer
|
image: adminer
|
||||||
|
container_name: fluksio-adminer
|
||||||
restart: always
|
restart: always
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
# Deliberately not on `proxy`: the Postgres UI is reachable from the host
|
||||||
|
# in dev only (see compose.dev.yml), never routed from the internet.
|
||||||
networks:
|
networks:
|
||||||
- traefik-public
|
|
||||||
- default
|
- default
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
environment:
|
environment:
|
||||||
- ADMINER_DESIGN=pepa-linha-dark
|
- ADMINER_DESIGN=pepa-linha-dark
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.docker.network=traefik-public
|
|
||||||
- traefik.constraint-label=traefik-public
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-adminer-http.rule=Host(`adminer.${DOMAIN?Variable not set}`)
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-adminer-http.entrypoints=http
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-adminer-http.middlewares=https-redirect
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.rule=Host(`adminer.${DOMAIN?Variable not set}`)
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.entrypoints=https
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.tls=true
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-adminer-https.tls.certresolver=le
|
|
||||||
- traefik.http.services.${STACK_NAME?Variable not set}-adminer.loadbalancer.server.port=8080
|
|
||||||
|
|
||||||
prestart:
|
prestart:
|
||||||
image: '${DOCKER_IMAGE_BACKEND?Variable not set}:${TAG-latest}'
|
image: '${DOCKER_IMAGE_BACKEND?Variable not set}:${TAG-latest}'
|
||||||
|
container_name: fluksio-prestart
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: backend/Dockerfile
|
dockerfile: backend/Dockerfile
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
networks:
|
networks:
|
||||||
- traefik-public
|
- proxy
|
||||||
- default
|
- default
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -56,7 +61,7 @@ services:
|
|||||||
restart: true
|
restart: true
|
||||||
command: bash scripts/prestart.sh
|
command: bash scripts/prestart.sh
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- ../.env
|
||||||
environment:
|
environment:
|
||||||
- DOMAIN=${DOMAIN}
|
- DOMAIN=${DOMAIN}
|
||||||
- FRONTEND_HOST=${FRONTEND_HOST?Variable not set}
|
- FRONTEND_HOST=${FRONTEND_HOST?Variable not set}
|
||||||
@@ -78,9 +83,12 @@ services:
|
|||||||
|
|
||||||
backend:
|
backend:
|
||||||
image: '${DOCKER_IMAGE_BACKEND?Variable not set}:${TAG-latest}'
|
image: '${DOCKER_IMAGE_BACKEND?Variable not set}:${TAG-latest}'
|
||||||
|
container_name: fluksio-api
|
||||||
restart: always
|
restart: always
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
networks:
|
networks:
|
||||||
- traefik-public
|
- proxy
|
||||||
- default
|
- default
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -89,7 +97,7 @@ services:
|
|||||||
prestart:
|
prestart:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- ../.env
|
||||||
environment:
|
environment:
|
||||||
- DOMAIN=${DOMAIN}
|
- DOMAIN=${DOMAIN}
|
||||||
- FRONTEND_HOST=${FRONTEND_HOST?Variable not set}
|
- FRONTEND_HOST=${FRONTEND_HOST?Variable not set}
|
||||||
@@ -116,12 +124,12 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: backend/Dockerfile
|
dockerfile: backend/Dockerfile
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.docker.network=traefik-public
|
- traefik.docker.network=proxy
|
||||||
- traefik.constraint-label=traefik-public
|
- traefik.constraint-label=proxy
|
||||||
|
|
||||||
- traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=8000
|
- traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=8000
|
||||||
|
|
||||||
@@ -133,42 +141,45 @@ services:
|
|||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls=true
|
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls=true
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls.certresolver=le
|
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls.certresolver=le
|
||||||
|
|
||||||
# Enable redirection for HTTP and HTTPS
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.middlewares=https-redirect
|
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.middlewares=https-redirect
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: '${DOCKER_IMAGE_FRONTEND?Variable not set}:${TAG-latest}'
|
image: '${DOCKER_IMAGE_FRONTEND?Variable not set}:${TAG-latest}'
|
||||||
|
container_name: fluksio-app
|
||||||
restart: always
|
restart: always
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
networks:
|
networks:
|
||||||
- traefik-public
|
- proxy
|
||||||
- default
|
- default
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: frontend/Dockerfile
|
dockerfile: frontend/Dockerfile
|
||||||
args:
|
args:
|
||||||
- VITE_API_URL=https://api.${DOMAIN?Variable not set}
|
- VITE_API_URL=https://api.${DOMAIN?Variable not set}
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.docker.network=traefik-public
|
- traefik.docker.network=proxy
|
||||||
- traefik.constraint-label=traefik-public
|
- traefik.constraint-label=proxy
|
||||||
|
|
||||||
- traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80
|
- traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80
|
||||||
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=Host(`dashboard.${DOMAIN?Variable not set}`)
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=Host(`app.${DOMAIN?Variable not set}`)
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.entrypoints=http
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.entrypoints=http
|
||||||
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.rule=Host(`dashboard.${DOMAIN?Variable not set}`)
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.rule=Host(`app.${DOMAIN?Variable not set}`)
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.entrypoints=https
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.entrypoints=https
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls=true
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls=true
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls.certresolver=le
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls.certresolver=le
|
||||||
|
|
||||||
# Enable redirection for HTTP and HTTPS
|
|
||||||
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.middlewares=https-redirect
|
- traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.middlewares=https-redirect
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
app-db-data:
|
app-db-data:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
traefik-public:
|
# Shared with the website stack and whatever reverse proxy fronts them.
|
||||||
# Allow setting it to false for testing
|
# Created once by the workspace root's scripts/setup.sh.
|
||||||
|
proxy:
|
||||||
external: true
|
external: true
|
||||||
Reference in New Issue
Block a user