Debugging
Verbose logging
Section titled “Verbose logging”Log verbosity is a runtime setting, not an environment variable. The file_log_level setting (debug, info, warn, or error; default info) controls what is written to the log files under <data-dir>/logs/. Change it from Admin > Logs in the web UI, or via odeion config edit under the logs section. Changes take effect immediately, without a restart.
Console output (stderr) always shows warnings and errors only; the level setting applies to the log files.
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. |
--pprof | false | Capture CPU/heap/goroutine profiles into pprof/. From the CLI this profiles the short-lived doctor process, not the server, so it is rarely useful; capture profiles from the admin UI instead. |
--pprof-seconds | 30 | CPU profile duration when --pprof is set. |
Redaction
Section titled “Redaction”Redaction runs on every section except postgresql.conf and pg_hba.conf, which don’t contain credentials.
| Data | Becomes | Notes |
|---|---|---|
| Email addresses | [EMAIL] | |
Bearer <token> | Bearer [TOKEN] | |
| JWT-looking values | [JWT] | |
| Hex strings of 32+ characters | [SECRET] | Catches most API keys. |
| Public IPv4/IPv6 addresses | [IP] / [IPV6] | Loopback, RFC1918, and link-local addresses are preserved; they don’t identify users and help local debugging. |
Absolute paths under $HOME | ~/... | |
| Settings values with sensitive keys | [REDACTED:<length>] | Keys containing api_key, apikey, password, passwd, secret, token, jwt, webhook, or auth. The values are also scrubbed from logs if they were ever 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.