Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 128 additions & 66 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,66 +1,128 @@
# Use the below flags to enable the Analytics or ActivityPub containers as well
# COMPOSE_PROFILES=analytics,activitypub

# Ghost domain
# Custom public domain Ghost will run on
DOMAIN=example.com

# Ghost Admin domain
# If you have Ghost Admin setup on a separate domain uncomment the line below and add the domain
# You also need to uncomment the corresponding block in your Caddyfile
# ADMIN_DOMAIN=

# Ghost ports
# Ports where Ghost will listen for HTTP traffic.
# Change these if the default ports are in use, or if Ghost is behind a reverse proxy.
HTTP_PORT=80
HTTPS_PORT=443

# Database settings
# All database settings must not be changed once the database is initialised
DATABASE_ROOT_PASSWORD=reallysecurerootpassword
# DATABASE_USER=optionalusername
DATABASE_PASSWORD=ghostpassword

# ActivityPub
# If you'd prefer to self-host ActivityPub yourself uncomment the line below
# ACTIVITYPUB_TARGET=activitypub:8080

# Tinybird configuration
# If you want to run Analytics, paste the output from `docker compose run --rm tinybird-login get-tokens` below
# TINYBIRD_API_URL=https://api.tinybird.co
# TINYBIRD_TRACKER_TOKEN=p.eyJxxxxx
# TINYBIRD_ADMIN_TOKEN=p.eyJxxxxx
# TINYBIRD_WORKSPACE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# Ghost configuration (https://ghost.org/docs/config/)

# SMTP Email (https://ghost.org/docs/config/#mail)
# Transactional email is required for logins, account creation (staff invites), password resets and other features
# This is not related to bulk mail / newsletter sending
mail__transport=SMTP
mail__options__host=smtp.example.com
mail__options__port=465
mail__options__secure=true
mail__options__auth__user=support@example.com
mail__options__auth__pass=1234567890
mail__from="'Acme Support' <support@example.com>"

# Advanced customizations

# Force Ghost version
# You should only do this if you need to pin a specific version
# The update commands won't work
# GHOST_VERSION=6-alpine

# Port Ghost should listen on
# You should only need to edit this if you want to host
# multiple sites on the same server
# GHOST_PORT=2368

# Data locations
# Location to store uploaded data
UPLOAD_LOCATION=./data/ghost

# Location for database data
MYSQL_DATA_LOCATION=./data/mysql
# Compose and operator settings.
#
# This file is read by Docker Compose for `${...}` interpolation. It is NEVER
# passed into the Ghost container: it holds infrastructure credentials that
# Ghost must not receive. Ghost's own configuration lives in `ghost.env`
# (see `ghost.env.example`).
#
# Values are written by the tooling in double-quoted form. If you edit by hand,
# remember that Compose interpolates unquoted and double-quoted values: write a
# literal dollar sign as `$$`. `scripts/config.sh set .env KEY VALUE` does this
# for you, and `scripts/config.sh validate` reports values that would be
# interpolated by accident.

# --- Site mode -------------------------------------------------------------
# Exactly one site mode must be selected: `local` or `production`.
# Optional per-site profiles are added to the same list: `analytics`,
# `activitypub`. Profiles are additive; adding an optional profile never
# changes the site mode.
COMPOSE_PROFILES="production"
SITE_MODE="production"

# Stable project identity. Kept independent of the directory name so a site can
# be moved. Also the suffix of the unique service aliases
# (`ghost-${COMPOSE_PROJECT_NAME}` and friends) used by generated proxy routes.
COMPOSE_PROJECT_NAME="ghost-example-com"

# Absolute path of this site directory. Moving a site requires updating this
# and re-validating the bind mounts.
PROJECT_DIR="/opt/ghost/example.com"

# --- Ghost ----------------------------------------------------------------
NODE_ENV="production"

# Public URL of the site, without a trailing slash.
# production: https://example.com
# local: http://localhost:2368
URL="https://example.com"

# Public domain served by Caddy. Production only.
DOMAIN="example.com"

# Optional separate Ghost Admin domain. Leave unset when there is none.
# ADMIN_DOMAIN="admin.example.com"
# ADMIN_URL="https://admin.example.com"

# Optional `www.` redirect target rendered into the generated Caddy routes.
# WWW_REDIRECT="www.example.com"

# Exact Ghost image pin, resolved at installation. The `next` variants install
# Ghost directly under /home/ghost rather than the older
# /var/lib/ghost/versions/<v> layout.
GHOST_IMAGE="ghost"
GHOST_VERSION="6-next-alpine"

# Paths inside the Ghost image. The defaults match the `next` variants. Pinning
# a GHOST_VERSION with the older layout means setting both of these to
# /var/lib/ghost/content and /var/lib/ghost/current/core/server/data/tinybird.
# GHOST_CONTENT_PATH="/home/ghost/content"
# GHOST_TINYBIRD_PATH="/home/ghost/core/server/data/tinybird"

# Ghost is always published on the loopback interface only, so a
# bring-your-own reverse proxy can reach it without exposing it publicly.
GHOST_PORT="2368"

# Readiness probe path. Change this only for a subdirectory install.
# GHOST_HEALTHCHECK_PATH="/ghost/api/admin/site/"

# --- Ingress (production) --------------------------------------------------
HTTP_PORT="80"
HTTPS_PORT="443"

# --- Lifecycle ------------------------------------------------------------
# Applied to long-running services only. One-shot jobs keep `restart: "no"`.
# production: unless-stopped
# local: no
RESTART_POLICY="unless-stopped"

# --- Database -------------------------------------------------------------
# Parameterized now so backup, restore and import all share one connection
# contract. The defaults are correct for a single-site installation.
DATABASE_HOST="db"
DATABASE_PORT="3306"
DATABASE_NAME="ghost"
DATABASE_USER="ghost"
DATABASE_PASSWORD="change-me-application-password"

# Infrastructure credential. Ghost never receives this.
DATABASE_ROOT_PASSWORD="change-me-root-password"

# Extra databases created on first initialisation.
DATABASE_EXTRA_DATABASES="activitypub"

# --- Data locations -------------------------------------------------------
UPLOAD_LOCATION="./data/ghost"
MYSQL_DATA_LOCATION="./data/mysql"

# --- Container logs -------------------------------------------------------
# Container logs are capped so a long-running site cannot fill the disk.
LOG_DRIVER="json-file"
LOG_MAX_SIZE="10m"
LOG_MAX_FILE="3"

# --- ActivityPub (optional, per-site) -------------------------------------
# Add `activitypub` to COMPOSE_PROFILES to run this site's own ActivityPub
# service, its migration job and its database. Each site owns its ActivityPub
# database, storage and serving URL.
#
# Resource cost: one long-running Node service (~150-250 MB RSS), one one-shot
# migration job per start, and one extra MySQL database.
#
# ACTIVITYPUB_DATABASE_NAME="activitypub"
# Used only when the `activitypub` profile is NOT enabled, to point the
# generated routes at the hosted service.
# ACTIVITYPUB_TARGET="https://ap.ghost.org"

# --- Analytics (optional, per-site) ---------------------------------------
# Add `analytics` to COMPOSE_PROFILES. Tinybird credentials, workspace
# selection and schema deployment belong to this site; see TINYBIRD.md.
#
# Resource cost: one long-running proxy service (~100-200 MB RSS) plus the
# one-shot Tinybird login/sync/deploy jobs, and a Tinybird workspace.
#
# TINYBIRD_API_URL="https://api.tinybird.co"
# TINYBIRD_TRACKER_TOKEN="p.eyJxxxxx"
# TINYBIRD_ADMIN_TOKEN="p.eyJxxxxx"
# TINYBIRD_WORKSPACE_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# SALT_STORE_TYPE="file"
# TRAFFIC_ANALYTICS_LOG_LEVEL="info"
2 changes: 1 addition & 1 deletion .github/workflows/shellcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run ShellCheck
run: find . -type f -name "*.sh" -exec shellcheck {} +
run: find . -type f -name "*.sh" -not -path "./.git/*" -exec shellcheck {} +
64 changes: 64 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
name: "Tests"
on:
pull_request:
push:
branches:
- main
- renovate/*

env:
# Declared minimum, kept in sync with GD_MIN_COMPOSE_VERSION in
# scripts/lib/compose.sh.
MIN_COMPOSE_VERSION: "2.24.0"

jobs:
test:
name: Helpers and mode matrix
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: "22"

- name: Check the runtime prerequisites
run: |
set -eu
jq --version
docker version
docker compose version

- name: Install the minimum supported Docker Compose
run: |
set -eu
mkdir -p "$RUNNER_TEMP/min-compose"
curl -fsSL -o "$RUNNER_TEMP/min-compose/docker-compose" \
"https://github.com/docker/compose/releases/download/v${MIN_COMPOSE_VERSION}/docker-compose-linux-x86_64"
chmod +x "$RUNNER_TEMP/min-compose/docker-compose"
"$RUNNER_TEMP/min-compose/docker-compose" version

- name: Run the test suite
env:
GD_TEST_MIN_COMPOSE: ${{ runner.temp }}/min-compose/docker-compose
run: node --test --test-timeout=120000 tests/*.test.mjs

ingress:
name: Ingress smoke tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: "22"

- name: Run the local and production ingress smoke tests
env:
GD_TEST_INGRESS: "1"
run: node --test --test-timeout=900000 tests/ingress.test.mjs

- name: Show service logs on failure
if: failure()
run: docker ps -a && docker compose ls || true
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,23 @@ typings/

# Where we store docker data by default
data

# Ghost Docker generated/operator files
# Application configuration (credentials)
ghost.env
# Installation metadata
.ghost-docker.json
# Generated and operator-managed Caddy routes, and the validation staging tree
caddy/sites/*
!caddy/sites/.gitignore
caddy/custom/*
!caddy/custom/.gitignore
!caddy/custom/README.md
caddy/global/*
!caddy/global/.gitignore
!caddy/global/README.md
caddy/.staging/
# Operator's own Caddyfile from pre-1.0 installations (see the S6 migration)
caddy/Caddyfile.local
# Backups written by the helpers
*.bak.*
4 changes: 4 additions & 0 deletions .shellcheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Follow `.` / `source` directives so the shared libraries in scripts/lib are
# analysed together with their callers.
external-sources=true
source-path=SCRIPTDIR
Loading