initial commit
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# ============================================================================
|
||||
# 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:
|
||||
Reference in New Issue
Block a user