Running a site
Deploying
A Gansey deployment is four things:
- the
ganseyexecutable for its platform (Linux x86_64, Linux aarch64 or macOS), with the admin and the bundled assets built in; - its configuration: a
gansey.toml, environment variables, or both (see Configuration); - a Postgres database (14 or newer) that holds this site and nothing else;
- a
data/folder the process can write to.
One binary serves one site. The public pages, the JSON API under /api and the admin under /admin/ all come from the same process, so two sites means two processes, two databases and two data folders.
The data folder
With the default settings everything the site writes lives under data/:
| Path | What it holds | Setting |
|---|---|---|
data/media/ | uploads and their generated sizes | [media].root |
data/module-files/ | files modules keep, such as form uploads and release files | [modules].files_dir, else module-files beside the media folder |
data/modules/ | installed modules, one folder each | [modules].dir |
data/backups/ | backups, one folder each | [backups].dir |
data/logs/ | daily JSON log files | [logs].dir |
data/hub-public-key | the hub's release signing key, pinned on first use | beside the media folder |
data/licence-check.json | the last licence answer, kept for the grace period | beside the media folder |
data/signing.key | the site's own signing key, only on sites that sign files | [signing].key_file |
Relative paths are resolved against the process's working directory, not the folder gansey.toml sits in, and gansey.toml is looked for in the working directory unless --config names it. Start the binary from the site's folder, or use absolute paths throughout.
Binding and ports
The server listens on [server].bind and [server].port, 127.0.0.1:8080 by default. Gansey speaks plain HTTP and does not terminate TLS itself. Keep it on the loopback address when the reverse proxy runs on the same machine; bind 0.0.0.0 only when the proxy reaches it over a private network or from outside a container.
gansey serve --migrate --bind 127.0.0.1 --port 8080
The public URL
Set [auth].public_url (or GANSEY_PUBLIC_URL, or the short form GANSEY_URL) to the address visitors use, such as https://example.test. Gansey uses it for links in emails, canonical link tags, the sitemap and the Sitemap line in robots.txt, to decide whether the session cookie is marked Secure, and as part of the session cookie's name. Without it the site falls back to http://<bind>:<port>, which is right locally and wrong behind a proxy. Changing it later signs everyone out (see Security).
Behind a reverse proxy
Put a reverse proxy in front of the site to terminate TLS and forward requests to the bind address. Gansey does not read X-Forwarded-Proto or X-Forwarded-Host: it learns its public address from public_url alone. Configure the proxy to:
- pass the original
Hostheader through. When a browser does not sendSec-Fetch-Site, core compares theOriginheader withHostto spot cross-site posts to module routes. - set
X-Forwarded-Forto the visitor's address, replacing any value the visitor sent. Gansey reads the first address inX-Forwarded-For(orX-Real-IPwhen that is absent), and only when the connection itself comes from a loopback or private-network address; a connection from a public address is taken at its word. That address is what module routes are told and what their rate limits count. - accept request bodies large enough for uploads: up to 64 MB for the media library, 256 MB for theme kit imports and 512 MB for module routes.
Gansey compresses responses itself (gzip and brotli), so the proxy does not need to.
The container image
The repository's Dockerfile builds a Linux image that runs gansey serve --migrate as an unprivileged gansey user in /var/lib/gansey. It listens on 0.0.0.0:8080, keeps its data in /var/lib/gansey/data (mount a volume there), ships pg_dump and pg_restore for Postgres 17 and git for modules installed from a repository, and checks /api/health every 10 seconds. Configure it with environment variables such as DATABASE_URL, GANSEY_ENV and GANSEY_URL. The binary sits in /usr/local/bin, which the gansey user cannot write to, so a container cannot update itself: replace the image instead.
Running as a service
Whatever starts the process:
- run
gansey serve --migratefrom the site's folder, as an unprivileged user that owns the data folder. Keeping--migratemeans a new version applies its migrations as it starts. - restart it when it exits.
- let that user write to the folder that holds the executable, if the site updates itself. Installing an update renames files there.
- stop it with SIGINT where you can. The server finishes requests in flight on SIGINT (Ctrl+C); other stop signals end it at once.
An update installed from the admin replaces the running process with the new version in place (the process id stays the same). After gansey self-update on the command line, restart the service yourself.
Checking an installation
gansey doctor checks what the site needs and prints one line per check: the database connection, how many migrations have run, whether any users exist, the media folder, the logs folder, pg_dump, pg_restore and tar on the PATH, whether email is set up, and the public URL. It exits with status 1 when anything fails, so it fits in a deploy script.
gansey --config /srv/example/gansey.toml doctor