Browse Source

production docker

test/local-setup
Silberengel 3 weeks ago
parent
commit
f1d4dd8cb8
  1. 15
      .env.prod.example
  2. 118
      compose.prod.yml
  3. 89
      scripts/deploy_prod.sh

15
.env.prod.example

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
# Copy to .env.prod, fill in, then: set -a && source .env.prod && set +a
# Or use: export $(grep -v '^#' .env.prod | xargs)
POSTGRES_HOST=postgres
POSTGRES_DB=gc_index_relay_prod
POSTGRES_USER=postgres
POSTGRES_PASSWORD=change-me
POSTGRES_RUNTIME_USER=gc_index_relay
POSTGRES_RUNTIME_PASSWORD=change-me-runtime
SECRET_KEY_BASE=generate-with-mix-phx-gen-secret
PHX_HOST=gc-http-relay.imwald.eu
# Optional: pin release image (default latest)
# TAG=0.2.0

118
compose.prod.yml

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
# Production stack: Apache (on the host) terminates TLS and proxies to 127.0.0.1:4000.
#
# Helper script: ./scripts/deploy_prod.sh --help
#
# --- Local: build and push ---
# cp .env.prod.example .env.prod && edit secrets
# export TAG=0.2.0 # optional; relay/migrator use :latest if unset
# docker login
# ./scripts/deploy_prod.sh build-push
#
# --- Remote: pull and run ---
# ./scripts/deploy_prod.sh deploy
#
# Images (repository: silberengel/gc-http-relay):
# :${TAG} — Phoenix release (relay + migrator)
# :setup — one-shot DB user bootstrap (tag is literal "setup")
services:
postgres:
image: docker.io/apache/age:release_PG17_1.6.0
restart: unless-stopped
user: 1000:1000
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB in .env.prod}
POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER in .env.prod}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env.prod}
command: >
postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
cpus: "1.00"
memory: 1G
reservations:
cpus: "0.50"
memory: 512M
networks:
- internal
setup:
image: docker.io/silberengel/gc-http-relay:setup
build:
context: .
dockerfile: ./docker/setup.Dockerfile
command: ["/usr/local/bin/usersetup.sh"]
restart: "no"
depends_on:
postgres:
condition: service_healthy
environment:
POSTGRES_HOST: ${POSTGRES_HOST:-postgres}
POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER in .env.prod}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env.prod}
POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB in .env.prod}
POSTGRES_RUNTIME_USER: ${POSTGRES_RUNTIME_USER:?set POSTGRES_RUNTIME_USER in .env.prod}
POSTGRES_RUNTIME_PASSWORD: ${POSTGRES_RUNTIME_PASSWORD:?set POSTGRES_RUNTIME_PASSWORD in .env.prod}
networks:
- internal
migrator:
image: docker.io/silberengel/gc-http-relay:${TAG:-latest}
build:
context: .
dockerfile: ./docker/server.Dockerfile
command: ["/app/bin/migrate"]
restart: "no"
depends_on:
postgres:
condition: service_healthy
setup:
condition: service_completed_successfully
environment:
DATABASE_URL: "ecto://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}/${POSTGRES_DB}"
SECRET_KEY_BASE: ${SECRET_KEY_BASE:?set SECRET_KEY_BASE in .env.prod}
networks:
- internal
relay:
image: docker.io/silberengel/gc-http-relay:${TAG:-latest}
build:
context: .
dockerfile: ./docker/server.Dockerfile
command: ["/app/bin/server"]
restart: unless-stopped
deploy:
resources:
limits:
cpus: "1.00"
memory: 1G
reservations:
cpus: "0.50"
memory: 512M
depends_on:
postgres:
condition: service_healthy
migrator:
condition: service_completed_successfully
ports:
- "127.0.0.1:4000:4000"
environment:
DATABASE_URL: "ecto://${POSTGRES_RUNTIME_USER}:${POSTGRES_RUNTIME_PASSWORD}@${POSTGRES_HOST:-postgres}/${POSTGRES_DB}"
SECRET_KEY_BASE: ${SECRET_KEY_BASE:?set SECRET_KEY_BASE in .env.prod}
PHX_HOST: ${PHX_HOST:?set PHX_HOST in .env.prod (public hostname, no scheme)}
networks:
- internal
networks:
internal:
volumes:
pgdata:

89
scripts/deploy_prod.sh

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
#!/usr/bin/env bash
# Production Docker Compose helper — run ./scripts/deploy_prod.sh --help
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
COMPOSE="${COMPOSE:-docker compose}"
COMPOSE_FILE="${COMPOSE_FILE:-compose.prod.yml}"
ENV_FILE="${ENV_FILE:-.env.prod}"
APP_IMAGES=(setup relay migrator)
usage() {
cat <<'EOF'
Production Docker Compose helper (compose.prod.yml).
./scripts/deploy_prod.sh build build app images (setup, relay, migrator)
./scripts/deploy_prod.sh push push those images (run docker login first)
./scripts/deploy_prod.sh build-push build then push
./scripts/deploy_prod.sh pull pull images
./scripts/deploy_prod.sh up start stack (detached)
./scripts/deploy_prod.sh deploy pull then up -d (typical on server)
./scripts/deploy_prod.sh down stop stack
./scripts/deploy_prod.sh ps docker compose ps
./scripts/deploy_prod.sh logs [svc] follow logs (default: all services)
Env: ENV_FILE (default .env.prod), COMPOSE_FILE, TAG, COMPOSE (default "docker compose")
EOF
exit "${1:-0}"
}
require_env_file() {
if [[ ! -f "$ENV_FILE" ]]; then
echo "error: missing env file: $ENV_FILE" >&2
echo " cp .env.prod.example .env.prod && edit, or set ENV_FILE=..." >&2
exit 1
fi
}
compose() {
require_env_file
$COMPOSE --env-file "$ENV_FILE" -f "$COMPOSE_FILE" "$@"
}
cmd="${1:-}"
[[ -z "$cmd" ]] && usage 1
[[ "$cmd" == "-h" || "$cmd" == "--help" ]] && usage 0
shift || true
case "$cmd" in
help)
usage 0
;;
build)
compose build "${APP_IMAGES[@]}"
;;
push)
compose push "${APP_IMAGES[@]}"
;;
build-push)
compose build "${APP_IMAGES[@]}"
compose push "${APP_IMAGES[@]}"
;;
pull)
compose pull
;;
up)
compose up -d
;;
deploy)
compose pull
compose up -d
;;
down)
compose down
;;
ps)
compose ps "$@"
;;
logs)
compose logs -f "$@"
;;
*)
echo "error: unknown command: $cmd" >&2
usage 1
;;
esac
Loading…
Cancel
Save