Debugging
Verbose logging
Section titled “Verbose logging”For general debugging without profiling, you can increase log verbosity with the ODEION_LOG_LEVEL environment variable:
ODEION_LOG_LEVEL=debug odeion serve --database-url localSee Configuration for all startup configuration options.
Diagnostic bundles
Section titled “Diagnostic bundles”odeion doctor collects information about your install into a single zip that you can attach to a bug report or send to support. The bundle is redacted by default and can be encrypted with age so it’s safe to send over any channel.
Basic usage
Section titled “Basic usage”odeion doctorThis writes odeion-doctor-<uuid>.zip into <data-dir>/diagnostics/ and prints the absolute path on success. In the Docker image that’s /var/lib/odeion/diagnostics/.
doctor expects the Odeion server to be running. It connects to the embedded postgres using the read-only role (odeion_ro) and only runs SELECT queries, so it can’t perturb the running instance. If the server is not running, the postgres_live section fails cleanly and the rest of the bundle (system info, on-disk postgres metadata, logs) is still written.
If the server itself is broken and you can’t reach the database, pass --no-db to skip that section entirely:
odeion doctor --no-dbWhat’s in the bundle
Section titled “What’s in the bundle”odeion-doctor-<uuid>.zip├── manifest.json Sections collected, flags used, per-section status.├── README.txt Human-readable summary.├── system/ OS, kernel, CPU, disk free, ffmpeg/ffprobe versions.├── install/ Odeion build info and data-directory layout.├── postgres/│ ├── on_disk.json PG_VERSION, data-dir size, WAL segment count.│ ├── postgresql.conf Raw copy.│ ├── pg_hba.conf Raw copy.│ ├── server_stats.json Uptime, db size, connection counts, cache ratio.│ ├── migrations.json Applied and pending goose migrations.│ ├── table_stats.json Per-table row counts and sizes.│ └── top_statements.json Top 25 queries from pg_stat_statements.├── settings/│ └── system_settings.json All admin settings, sensitive values redacted.├── tasks/│ └── summary.json processing_tasks counts by type and status.└── logs/ ├── server.log Tail, redacted. └── postgres.log Tail, redacted.No table row data is ever included, only counts, sizes, and stats. The exception is system_settings, which holds the values you entered through the admin UI. Sensitive fields (API keys, passwords, tokens, webhook secrets) are replaced with [REDACTED:<length>] unless --unredacted is set.
By default the bundle includes every section. Use --no-* flags to opt out. --unredacted and --age-recipient are the only opt-in flags.
| Flag | Default | Description |
|---|---|---|
--data-dir | $ODEION_DATA_DIR | Data directory. Must match the serve process. |
--database-url | local | local for the embedded postgres managed by the running server, or a DSN for an external database. |
--out | <data-dir>/diagnostics/odeion-doctor-<uuid>.zip[.age] | Override the output path. .age is appended when encrypting. |
--no-logs | false | Exclude the logs/ section. |
--no-db | false | Skip postgres entirely. Use when the server is broken. |
--no-settings | false | Skip the system_settings dump. |
--no-task-summary | false | Skip processing_tasks row counts. |
--log-size | 10 MiB | Per-log-file byte cap. Only the tail of each log is included. |
--unredacted | false | Skip all redaction. See warning below. |
--age-recipient | unset | Encrypt the output with age for the given recipient. |
Redaction
Section titled “Redaction”Redaction runs on every section except postgresql.conf and pg_hba.conf, which don’t contain credentials.
Scrubbed by default:
- Email addresses become
[EMAIL]. Bearer <token>becomesBearer [TOKEN].- JWT-looking values become
[JWT]. - Hex strings of 32 characters or more become
[SECRET]. This catches most API keys. - Public IPv4 and IPv6 addresses become
[IP]or[IPV6]. Loopback, RFC1918, and link-local addresses are preserved because they don’t identify users and are useful for local debugging. - Absolute paths under
$HOMEare rewritten as~/.... - In the settings dump, any value whose key contains
api_key,apikey,password,passwd,secret,token,jwt,webhook, orauthis replaced with[REDACTED:<length>]. Those same values are also registered as literals so they won’t appear in logs even if they were printed.
Passing --unredacted disables all of the above. The bundle will contain real API keys, passwords, file paths, and email addresses. A warning is printed before collection runs. Only use this for local inspection, or pair it with --age-recipient when you need to ship sensitive data to support without exposing it in transit. Do not post an unredacted bundle to a public issue.
Encrypting
Section titled “Encrypting”When --age-recipient is set, the zip is encrypted with age for that recipient and the output file gets a .zip.age suffix. The plaintext zip never touches disk.
Accepted recipient formats:
- An age X25519 public key:
age1... - An SSH public key line:
ssh-ed25519 AAAAC3... name@hostorssh-rsa AAAAB3... name@host
The typical support flow:
# Encrypt to the support engineer's published public keyodeion doctor --age-recipient=age1qz...zk2
# Wrote: /var/lib/odeion/diagnostics/odeion-doctor-<uuid>.zip.age# Send that file to support.
# Support side (holder of the matching private key):age --decrypt --identity support.key \ odeion-doctor-<uuid>.zip.age > bundle.zipunzip bundle.zipEncryption and redaction are independent. A redacted bundle can also be encrypted for transport, and an unredacted bundle can be encrypted so that only the intended recipient can read it.
Sharing the bundle
Section titled “Sharing the bundle”A default (redacted, unencrypted) bundle is safe to attach to a GitHub issue or forum post. Before sharing, it’s still worth unzipping and skimming manifest.json and logs/server.log to confirm nothing surprising slipped through.
If manifest.json reports a section failure (for example because embedded postgres wasn’t reachable), the rest of the bundle is still written. Mention any reported section errors in your bug report so support knows what’s missing.