Files

129 lines
6.2 KiB
Markdown

# Backup and export
Beacon exports its PostgreSQL database and saved `config.yaml` as a private, versioned
`.tar.gz`, either with the standalone `beacon-backup` command or an opt-in admin download.
`beacon-backup -verify` checks an archive offline. There is no import, scheduling or remote
storage.
## Archive format 1
Exactly three regular files:
- `manifest.json`: format and exporter version, creation time, payload sizes and SHA-256
hashes, and the exclusions below.
- `database.sql`: one consistent `pg_dump` snapshot (schema, data, migration journal),
without ownership, ACLs or tablespaces so it restores under any role.
- `config.yaml`: the saved file byte-for-byte, read just before the snapshot. Avoid config
edits during an export.
**It is sensitive and unencrypted** (keys, message data); keep it private. It is not a full
recovery package: `.env` and environment variables, `borderFile` and TLS files, runtime-only
changes, PostgreSQL roles and cluster settings, Redis and deployment files are not included.
## Requirements and limits
- Keep the MeshMapper API key in `MESHMAPPER_API_KEY`, with `meshmapper.api_key` empty or
omitted in the saved YAML. Both CLI exports and admin downloads refuse a nonempty saved
MeshMapper key because the YAML is included verbatim. Environment secrets remain excluded;
preserve them separately for recovery. See [MeshMapper API key](configuration.md#meshmapper-api-key).
- `pg_dump` must be in the **same runtime** as the exporter (the CLI's environment, or the
server's PATH for the download), with a major version at least as new as the database's. A client on
the Docker host or in another container doesn't count.
- The Docker image ships the PostgreSQL 16 client. Its Alpine 3.19 base has no 17/18
packages, so a newer database needs a different runtime, not just `POSTGRES_CLIENT_MAJOR`.
- The temp directory (`TMPDIR`) needs about twice the SQL limit free. A small tmpfs won't do.
- The output filesystem must support hard links (used to publish atomically). Existing
destinations, symlinks included, are never replaced. Files are 0600 and staging dirs 0700;
on Windows, restrict the directory ACLs yourself.
| Limit | Default |
| --- | --- |
| Timeout | 10 min (`-timeout`) |
| Uncompressed SQL | 1 GiB (`-max-bytes`, up to 1 TiB) |
| Saved YAML / manifest | 1 MiB / 64 KiB |
| Compressed archive (verify) | SQL limit + 1% + 2 MiB |
| Lock wait | 5 s |
Failures, timeouts and interrupts clean up and publish nothing. A SIGKILL or power loss can
leave a private staging dir: `.beacon-backup-*` next to the CLI output, or
`beacon-download-*` in the server's temp dir, which Beacon sweeps on its next start.
## CLI export
In a beacon-server clone:
```sh
go build ./cmd/beacon-backup
./beacon-backup -config /private/config.yaml -output /private/beacon-20260913.tar.gz
```
Connection comes only from the standard libpq environment (`PGHOST`, `PGPORT`,
`PGDATABASE`, `PGUSER`, TLS settings); `PGDATABASE` is required. Keep the password in a 0600
`PGPASSFILE` or a service file. The command ignores `.env` and `POSTGRES_DSN`, runs no
migrations, and never prints `pg_dump` stderr since it can leak private data. If it fails,
check the client version, privileges, connection settings and free space.
## Download API
`GET /api/v1/admin/backup` streams a fresh archive. Enable it with `backup.enabled: true` and
the admin bearer key, then restart. Send the key only in the `Authorization` header over
HTTPS; the endpoint takes no query or body. Leave it off on public previews.
At startup Beacon checks `pg_dump --version` against the server. If that or another
prerequisite fails, it logs why and disables only backup; fix it and restart.
**Connection.** The download uses `POSTGRES_DSN`, which must be a single-host `postgres://`
URL with an explicit user and database. TLS options, `passfile`, `connect_timeout`,
`application_name`, `target_session_attrs` and `options` are passed through; `pool_*`
options are dropped; supported `PG*` env defaults apply under the URL. Keyword DSNs,
services, multiple hosts, unknown or duplicate options, protocol-negotiation settings and
the `PGSERVICE`, `PGSERVICEFILE`, `PGSSLNEGOTIATION`, `PG{MIN,MAX}PROTOCOLVERSION` and `PGTZ`
env vars are rejected. Use the CLI for those setups. Prefer `sslmode=verify-full` for remote
databases. Credentials go to a 0600 service file in staging, never into process arguments,
logs or errors.
**Behaviour.** One export at a time. Disconnecting cancels it, and the transfer has a
10-minute write deadline. Responses are `no-store`. A failed export sends no partial archive
and logs a sanitized reason.
| Status | Meaning |
| --- | --- |
| 400 | Query string or body sent |
| 401 | Missing or invalid key |
| 409 | Another export is running |
| 500 | Export failed |
| 503 | Backup unavailable, no admin key, or shutting down |
| 504 | Timed out |
| 507 | SQL size limit exceeded |
## Verify
```sh
./beacon-backup -verify /private/beacon-20260913.tar.gz
```
Reads the file without extracting, running SQL or connecting to anything, so it needs no
config, `pg_dump` or credentials. `-max-bytes` and `-timeout` apply. It requires exactly the
three members as 0600 regular files with USTAR headers (size-only PAX allowed for SQL over
8 GiB). It rejects duplicates, links, devices, extra metadata, truncated or concatenated
streams, and unknown or duplicate manifest keys. Sizes and SHA-256 hashes must match.
Exit 0 means structure and checksums passed. Errors don't include payload, manifest values
or the path. The manifest is unsigned, so a pass does **not** prove authenticity, safe SQL or
that the dump restores.
## Restore
Only restore trusted bundles, because a dump can run arbitrary SQL. Verify, extract
privately, and load into a **new, empty database** on a compatible PostgreSQL with the
needed extensions:
```sh
psql -X --set ON_ERROR_STOP=on --single-transaction --file database.sql
```
Check the migration journal and some records, review the config, and restore external
secrets and files before pointing a new Beacon at it. Never test an export by restoring over
live data. See [pg_dump](https://www.postgresql.org/docs/16/app-pgdump.html) and
[libpq environment](https://www.postgresql.org/docs/16/libpq-envars.html).