141 lines
6.2 KiB
YAML
141 lines
6.2 KiB
YAML
# ============================================================================
|
|
# SHARP-ER - full stack for local development, review and small deployments
|
|
# ----------------------------------------------------------------------------
|
|
# cp .env.example .env
|
|
# ./scripts/generate-secrets.sh --write .env
|
|
# docker compose up -d --build
|
|
#
|
|
# web http://localhost:3000 (redirects / to /en or /ar)
|
|
# cms http://localhost:1337/admin (create the first admin user here)
|
|
# db 127.0.0.1:5433 (5433 on purpose - see .env.example)
|
|
#
|
|
# Every ${VARIABLE} below is read from the .env file next to this one. The
|
|
# secrets use ${VAR:?...}, so a missing value stops the stack immediately with
|
|
# a readable message instead of a Strapi crash loop.
|
|
#
|
|
# Full walkthrough, including teardown and the CMS media caveat: README.md.
|
|
# ============================================================================
|
|
|
|
name: sharper
|
|
|
|
services:
|
|
# --------------------------------------------------------------------------
|
|
# PostgreSQL 16 - the CMS database. Data lives in a named volume, so
|
|
# `docker compose down` keeps it and only `down -v` deletes it.
|
|
# --------------------------------------------------------------------------
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_DB: ${DATABASE_NAME:-sharper_cms}
|
|
POSTGRES_USER: ${DATABASE_USERNAME:-postgres}
|
|
POSTGRES_PASSWORD: ${DATABASE_PASSWORD:?set DATABASE_PASSWORD in .env}
|
|
# Matches the manual path (createdb --encoding UTF8). Arabic content is
|
|
# stored as UTF-8 either way; being explicit keeps the two paths identical.
|
|
POSTGRES_INITDB_ARGS: "--encoding=UTF8"
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
ports:
|
|
# Published on 5433 by default so it cannot collide with a PostgreSQL
|
|
# already running on the host's 5432.
|
|
- "${POSTGRES_PUBLISHED_PORT:-5433}:5432"
|
|
healthcheck:
|
|
# pg_isready also fails while the entrypoint's temporary init server is
|
|
# up, so `service_healthy` really does mean "ready for connections".
|
|
test:
|
|
["CMD-SHELL", "pg_isready -U ${DATABASE_USERNAME:-postgres} -d ${DATABASE_NAME:-sharper_cms} -h 127.0.0.1"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 12
|
|
start_period: 15s
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Strapi 5 - REST API and admin panel. Waits for a database that is actually
|
|
# accepting connections, then runs its own migrations and the boot-time
|
|
# reconciliation in apps/cms/src/bootstrap (locales + public permissions).
|
|
# --------------------------------------------------------------------------
|
|
cms:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/cms/Dockerfile
|
|
image: sharper-cms:local
|
|
restart: unless-stopped
|
|
# The healthcheck this service is judged by (GET /_health) is declared in
|
|
# apps/cms/Dockerfile, so `docker run` gets it too.
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
environment:
|
|
NODE_ENV: production
|
|
HOST: 0.0.0.0
|
|
PORT: 1337
|
|
# Absolute URL the admin panel and generated links use. Change it
|
|
# together with CMS_PORT, and set it to the public origin in production.
|
|
PUBLIC_URL: ${CMS_PUBLIC_URL:-http://localhost:1337}
|
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
|
|
|
# Inside the compose network the database is reachable by service name on
|
|
# its own port, never through the published one.
|
|
DATABASE_CLIENT: postgres
|
|
DATABASE_HOST: postgres
|
|
DATABASE_PORT: 5432
|
|
DATABASE_NAME: ${DATABASE_NAME:-sharper_cms}
|
|
DATABASE_USERNAME: ${DATABASE_USERNAME:-postgres}
|
|
DATABASE_PASSWORD: ${DATABASE_PASSWORD:?set DATABASE_PASSWORD in .env}
|
|
DATABASE_SSL: "false"
|
|
|
|
# Generate all six with: ./scripts/generate-secrets.sh --write .env
|
|
APP_KEYS: ${APP_KEYS:?run ./scripts/generate-secrets.sh --write .env}
|
|
API_TOKEN_SALT: ${API_TOKEN_SALT:?run ./scripts/generate-secrets.sh --write .env}
|
|
ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET:?run ./scripts/generate-secrets.sh --write .env}
|
|
JWT_SECRET: ${JWT_SECRET:?run ./scripts/generate-secrets.sh --write .env}
|
|
TRANSFER_TOKEN_SALT: ${TRANSFER_TOKEN_SALT:?run ./scripts/generate-secrets.sh --write .env}
|
|
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?run ./scripts/generate-secrets.sh --write .env}
|
|
|
|
# Browser origins allowed to call the API directly.
|
|
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000}
|
|
volumes:
|
|
# Media Library files. Like the database, they survive `down` and are
|
|
# only removed by `down -v`.
|
|
- cms-uploads:/repo/apps/cms/public/uploads
|
|
ports:
|
|
- "${CMS_PORT:-1337}:1337"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# React Router 8 server-side renderer. Waits for a CMS that answers /_health,
|
|
# so the first page render already has content. (Pages do fall back to their
|
|
# bundled draft copy if the CMS goes away later - the site never 500s because
|
|
# Strapi is down.)
|
|
# --------------------------------------------------------------------------
|
|
web:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/web/Dockerfile
|
|
image: sharper-web:local
|
|
restart: unless-stopped
|
|
# Healthcheck (GET /en) is declared in apps/web/Dockerfile.
|
|
depends_on:
|
|
cms:
|
|
condition: service_healthy
|
|
environment:
|
|
NODE_ENV: production
|
|
PORT: 3000
|
|
# Server-side fetches go to the service name on the compose network.
|
|
# This origin is also prefixed onto uploaded media URLs that reach the
|
|
# browser - see "Images from the CMS" in README.md.
|
|
STRAPI_URL: ${DOCKER_STRAPI_URL:-http://cms:1337}
|
|
STRAPI_TOKEN: ${STRAPI_TOKEN:-}
|
|
# Canonical URLs, hreflang alternates and JSON-LD are built from this.
|
|
SITE_URL: ${SITE_URL:-http://localhost:3000}
|
|
volumes:
|
|
# Panton and the Arabic faces are licensed and deliberately excluded from
|
|
# the image (.dockerignore). Mounting the host folder read-only lets a
|
|
# local container use them without ever baking them into an artefact.
|
|
- ./apps/web/public/fonts:/repo/apps/web/build/client/fonts:ro
|
|
ports:
|
|
- "${WEB_PORT:-3000}:3000"
|
|
|
|
volumes:
|
|
postgres-data:
|
|
cms-uploads:
|