Skip to content

Configuration

Vexillum uses TOML configuration for application-level settings.

The configuration file defines how the Vexillum runtime starts, which services listen on which addresses, how storage is handled, and how verbose logging should be.

A typical Vexillum deployment uses a vexillum.toml file.

The file includes settings for:

  • Application runtime name
  • Logging
  • Storage
  • Admin web listener
  • Public API listener (including CORS and rate limiting)
  • Metrics listener

Keep this file under version control if possible, but do not commit secrets, shared passphrases, bootstrap credentials, or deployment-specific private data. Apparently we still have to say that out loud because the internet exists.

Operators can configure a runtime name for the Vexillum instance using the runtime.name setting. This name is used for identification in logs, metrics, and administrative interfaces.

[runtime]
name = "my-vexillum-instance"

Operators should configure logs so they are useful during troubleshooting without becoming an infinite scroll of packet noise.

Valid log levels include:

  • debug
  • info
  • warn
  • error

The default log level is info, which is usually a good starting point for production deployments. debug can be useful during initial setup or troubleshooting, but may be too verbose for long-term use.

Valid log formats include:

  • text
  • json

The default log format is json, which is structured and easier to parse with log management tools. text can be more human-readable when viewing logs directly, but may be less consistent for automated processing.

For systemd deployments, logs are commonly reviewed with:

journalctl -u vexillum -f

Vexillum uses SQLite DB storage for runtime and administrative data.

The database.url setting defines the storage location. The default is sqlite://vexillum.db, which creates a vexillum.db file in the current working directory.

Back up the storage location before upgrades, schema changes, or major configuration edits.

The admin web interface should usually be bound to a local or private address.

Recommended default:

[admin]
listen = "127.0.0.1:8080"

Binding the admin interface to localhost is safer when using a reverse proxy, VPN, SSH tunnel, or local-only administration model.

Avoid exposing the admin interface directly to the public internet unless you have reviewed authentication, TLS, firewalling, and access controls. Publicly exposing admin panels is how infrastructure turns into archaeology.

The metrics listener is intended for monitoring systems.

Recommended default:

[metrics]
listen = "127.0.0.1:9090"

Keep metrics private unless you intentionally want to publish them. Metrics can reveal operational details about your deployment, including service state and traffic patterns.

Vexillum has no bundled public dashboard. The public listener serves a single unauthenticated, read-only JSON REST + SSE API under /api/public/v1 for reflector status data (instance inventory, per-mode stats, connected clients, recent call history) - GET / just redirects to interactive Swagger docs at /api/public/v1/docs. See the Public API Reference for the full endpoint list and response shapes, and the Public Interface page for deployment guidance.

Everything for this listener lives in one [public_api] section - enabled/listen alongside CORS and rate-limit settings, since it’s all one feature:

[public_api]
enabled = true
listen = "127.0.0.1:8081"
cors_allowed_origins = []
rate_limit_per_minute = 120
rate_limit_burst = 20
  • enabled / listen - whether the listener runs at all, and the address it binds to. Set enabled = false to disable the public API entirely.
  • cors_allowed_origins - origins allowed to read the API cross-origin from a browser. Empty (the default) means same-origin/non-browser clients only; curl and server-to-server calls are unaffected either way. List your dashboard’s exact origin(s) (scheme://host[:port], no path), or use ["*"] to allow any origin.
  • rate_limit_per_minute / rate_limit_burst - per-client-IP token-bucket limit, on by default (120/min, burst 20). Set rate_limit_per_minute = 0 to disable it.

This whole section is optional - omitting it applies the defaults shown above.

This listener should usually be bound to a local or private address and served through a reverse proxy, same as the admin listener.

Recommended reverse proxies include:

  • Caddy
  • Nginx
  • Traefik
  • HAProxy

Vexillum includes practical default listener assignments for supported services and modes.

Service Default listener
Admin web 127.0.0.1:8080
Public API 127.0.0.1:8081
Metrics 127.0.0.1:9090
D-Star DExtra 0.0.0.0:30001
DMR 0.0.0.0:62031
M17 0.0.0.0:17000
NXDN 0.0.0.0:41400
P25 0.0.0.0:41000
YSF 0.0.0.0:42000
VAFM UDP 0.0.0.0:43000
VAFM TCP 0.0.0.0:43000

These defaults are intended to be useful starting points. Operators should adjust addresses and ports to match their deployment, firewall policy, reverse proxy setup, and local network layout.

There are no mode configuration sections. Mode instance configuration, including listen addresses, is stored in the database, seeded from the defaults above, and managed per-instance through the admin UI’s Instances section.

Expose only the ports required for the modes you actually use.

For example, if you are running only M17 and VAFM, there is no reason to expose DMR, NXDN, P25, YSF, or D-Star ports.

A simple deployment might expose:

Port Purpose
17000/udp M17
43000/udp VAFM UDP
43000/tcp VAFM TCP
443/tcp Public API or reverse proxy

Administrative and metrics ports should usually remain private.

A clean deployment usually separates services like this:

Service Exposure
Protocol listeners Public, if the reflector is public
Public API listener localhost endpoint to a separate dashboard service
Admin web interface Private, VPN, localhost, or reverse proxy protected
Metrics listener Private or monitoring-only

This separation makes it easier to publish useful information without giving the entire planet a button labeled “break my reflector.”

Before starting Vexillum in production or semi-production, verify:

  • The admin listener is not accidentally exposed.
  • The metrics listener is not accidentally exposed.
  • Only required mode ports are open.
  • The configured listener ports do not conflict with other services.
  • The storage path is persistent.
  • Logs are captured by your service manager.
  • Backups include configuration and storage.
  • Shared passphrases or secrets are not stored in public repositories.
  • Firewall rules match the enabled modes.

Some configuration changes may require restarting the Vexillum process.

After changing the configuration:

  1. Validate the TOML syntax.
  2. Restart Vexillum.
  3. Confirm the application starts cleanly.
  4. Check logs for listener or mode startup errors.
  5. Confirm expected ports are listening.
  6. Test clients against the enabled modes.

Useful checks:

ss -ltnup | grep vexillum
journalctl -u vexillum -n 100
[runtime]
name = "Vexillum Runtime"

[logging]
level = "info"
format = "json"

[database]
url = "sqlite://vexillum.db"

[admin]
enabled = true
listen = "127.0.0.1:8080"

[metrics]
enabled = true
listen = "127.0.0.1:9090"

[public_api]
enabled = true
listen = "127.0.0.1:8081"
cors_allowed_origins = []
rate_limit_per_minute = 120
rate_limit_burst = 20

Configuration behavior may change while Vexillum is under active development. Operators should check the example configuration and release notes when upgrading.