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
10 changes: 4 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ URL="https://example.com"
# ADMIN_URL="https://admin.example.com"

# Requested Ghost repository/tag. Installation pins the resolved artifact below.
# The `next` variants install Ghost directly under /home/ghost rather than the older
# /var/lib/ghost/versions/<v> layout.
# Install and import require the `next` layout; see docs/configuration.md.
GHOST_IMAGE="ghost"
GHOST_VERSION="6-next-alpine"

Expand All @@ -51,9 +50,8 @@ GHOST_VERSION="6-next-alpine"
# Without it, manually configured sites use GHOST_IMAGE:GHOST_VERSION.
# GHOST_IMAGE_REF="ghost@sha256:..."

# 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.
# Paths inside the Ghost image, derived by install from its declared layout.
# These defaults match the `next` variants.
# GHOST_CONTENT_PATH="/home/ghost/content"
# GHOST_TINYBIRD_PATH="/home/ghost/core/server/data/tinybird"

Expand All @@ -75,7 +73,7 @@ HTTPS_PORT="443"
RESTART_POLICY="unless-stopped"

# --- Database -------------------------------------------------------------
# Parameterized now so backup, restore and import all share one connection
# Backup, restore and import share this connection
# contract. The defaults are correct for a single-site installation.
DATABASE_HOST="db"
DATABASE_PORT="3306"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ name: "Manager image"
#
# A release is published when the Release workflow calls this one with its
# tag, or when a release tag is pushed by hand. The moving tags only resolve a
# channel to a release; a site always runs a digest (plan §2.7).
# channel to a release; a site always runs a digest (docs/architecture.md#releases-and-compatibility).
on:
push:
branches:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/launcher.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: "Served launcher"
# GitHub Pages from this workflow. There is no gh-pages branch: each deploy is
# the whole site, built from the release. The custom domain is set in the
# repository's Pages settings (a CNAME file is ignored for an Actions deploy).
# Only the newest release is served, and never by hand (plan §2.7). Then
# Only the newest release is served, and never by hand (docs/architecture.md#releases-and-compatibility). Then
# tests what is served, not the checkout's copy: it installs the newest beta,
# and an explicit --release.
#
Expand Down
266 changes: 36 additions & 230 deletions AGENTS.md

Large diffs are not rendered by default.

70 changes: 41 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
Configuration to run Ghost and its services with Docker Compose.

> **This is the `next-docker` development branch.** It is being rebuilt around
> a manager image: a small CLI in a container, started by a launcher that needs
> only Docker. It installs local and production sites, imports local Ghost-CLI
> sites, and updates between its own beta releases; backups and production
> imports are still to come. See [the plan](docs/ghost-cli-replacement.md) for
> what lands when. For a supported setup today, use the `main` branch.
> a manager image: a small CLI in a container, started by a bash launcher that
> uses Docker. It installs local and production sites, imports local Ghost-CLI
> sites, backs up and restores them, and updates image installations between
> beta releases. Production import and migration from the released `main` layout
> remain on the [roadmap](docs/ghost-cli-replacement.md). For a supported setup
> today, use the `main` branch.

## The launcher

Expand All @@ -22,8 +23,9 @@ curl -fsSL https://docker.ghost.org/install.sh | bash -s -- install --local
exact image digest, writes Caddy's routes, starts the site and
reaches it through its own ingress; a failed installation removes what it
created. `config`, `check`, `info`, `list` and `doctor` look after it
afterwards, and `self-update` moves it to a newer release of ghost-docker. See [docs/install.md](docs/install.md). The other commands in these
documents are the planned interface, and each arrives with its plan step.
afterwards. `backup` and `restore` protect its data; `self-update` moves image
installations to a newer stack release. Checkouts use Git and Compose directly.
See [docs/install.md](docs/install.md) for supported commands and recovery.

Everything runs in a container. From a clone of this repository the launcher
builds that image from the clone; anywhere else it uses the published one,
Expand All @@ -45,29 +47,20 @@ Linux. There is no native Windows launcher.
| [`caddy/`](caddy) | The tracked Caddyfile and snippets, and where the site's routes go |
| [`.env.example`](.env.example), [`ghost.env.example`](ghost.env.example) | Operator settings and Ghost application settings, deliberately separate |
| [`docs/install.md`](docs/install.md) | Installing a site, and how it is verified |
| [`docs/configuration.md`](docs/configuration.md) | The configuration contract: the two files, value encoding, site modes, metadata |
| [`docs/configuration.md`](docs/configuration.md) | The configuration contract: env files, value encoding, site modes and overrides |
| [`docs/caddy.md`](docs/caddy.md) | The site's routes, and how to change them |
| [`docs/bundle-v1.md`](docs/bundle-v1.md) | The Ghost-CLI migration bundle format |
| [`scripts/`](scripts) | Release tooling the workflows run: cutting a release, moving tags, release notes |
| [`pages/`](pages) | The front page of docker.ghost.org, published with each release beside `install.sh` |
| [`docs/ghost-cli-replacement.md`](docs/ghost-cli-replacement.md) | The plan: architecture, contracts, and steps |
| [`docs/architecture.md`](docs/architecture.md) | Current manager boundaries, state ownership and recovery invariants |
| [`docs/ghost-cli-replacement.md`](docs/ghost-cli-replacement.md) | Remaining requirements and release gates |

## Site modes

Exactly one site mode is selected through `COMPOSE_PROFILES`:

```sh
# Local: Ghost + MySQL, published on 127.0.0.1:${GHOST_PORT}
COMPOSE_PROFILES=local docker compose up -d

# Production: Ghost + MySQL + Caddy with automatic HTTPS
COMPOSE_PROFILES=production docker compose up -d
```

Optional per-site profiles are additive: `analytics`, `activitypub`.

`./ghost-docker install` sets them, and writes a production site's routes into
`caddy/sites/site.caddy` once; the file is yours from then on.
`install --local` selects Ghost and MySQL on loopback. A production install
adds Caddy with automatic HTTPS. [Configuration](docs/configuration.md#site-modes-and-profiles)
defines the modes and optional profiles; [installation](docs/install.md#optional-services)
explains how to enable them.

## Requirements

Expand All @@ -76,19 +69,38 @@ launcher. Nothing else on the host. Windows is supported through WSL2.

## Developing

Use Node 26 and the pnpm version pinned by each package (through Corepack).
The repository has two independent packages, `manager/` and `scripts/`; there is
no root bootstrap or check command.

From `manager/`:

```sh
cd manager && pnpm install # pnpm via corepack: npm i -g corepack && corepack enable
pnpm run format:check && pnpm run lint && pnpm run typecheck && pnpm test
pnpm install --frozen-lockfile
pnpm run format:check
pnpm run lint
pnpm run typecheck
pnpm test
pnpm run test:integration # real daemon, MySQL and Caddy; runs in a container
```

manager/test/integration/run.sh # the manager against the real daemon, MySQL and Caddy
From `scripts/`, run `pnpm install --frozen-lockfile`, `pnpm run typecheck` and
`pnpm test` for release-tool changes. From the repository root:

tests/e2e/launcher.sh # the launcher against a stand-in docker, then the real image
```sh
tests/e2e/launcher.sh # stand-in Docker, then the real image
tests/e2e/install.sh # real installations: pulls images, binds 80 and 443
tests/e2e/self-update.sh # real updates between releases built here; a clone refused, backed up and restored
tests/e2e/backup.sh # real backups and restores, with ActivityPub
tests/e2e/self-update.sh # release updates, failed-update write retention and recovery
tests/e2e/backup.sh # real backups and restores, including ActivityPub
tests/e2e/import.sh # real Ghost-CLI sites exported and imported
```

Shell changes also pass ShellCheck. Unit tests substitute `Io` at the Engine API,
Compose and service-client boundaries (`manager/test/helpers.ts`). Integration
tests use the manager Dockerfile's `integration` stage to exercise real Compose
and network attachment. E2E tests check operator-visible outcomes. New tests
should prove behavior at the appropriate boundary, not duplicate the code path.

An e2e script fails when this host cannot run one of its scenarios (no
Docker, port 80 or 443 already held, no `mysqldump`), so a passing run, and
CI, ran everything. `GD_E2E_ALLOW_SKIP=1` skips those on purpose instead, and
Expand Down
Loading
Loading