Skip to content

Configuration

The Organization Manager is configured by a single YAML file. The shipped systemd unit passes /etc/pdns-pf/orgmanager.yaml to the binary; a heavily-commented template ships alongside it as /etc/pdns-pf/orgmanager.example.yaml.

Environment variables in the YAML are always expanded before parsing: $VAR and ${VAR} references get replaced with their values from the service's environment. This is the recommended way to inject secrets — most importantly the PostgreSQL password — from a systemd drop-in rather than hard-coding them in the file. There is no flag to turn this off.

The PostgreSQL DSN is masked in the startup log line that dumps the effective config, so credentials passed in via env-var expansion do not end up in journalctl.

Every config option is documented on this page. Use your browser's in-page search (Ctrl/⌘-F) to jump straight to the field you care about.

Top-level structure

Parameter Type Default Description
http HTTP {} REST API listener, status page, Prometheus /metrics, /health.
database Database {} PostgreSQL connection. Required — the service refuses to start without it.
nats NATS {request_timeout: 10s} NATS transport for publishing session updates to the IP Mapper. Leaving the URL empty disables publishing.
deploy Deploy see below Asynchronous deployment worker pool and the two periodic timers.
log Log see Common configuration Log level and format.

deploy defaults to {workers: 4, version_check_interval: 60s, full_redeploy_interval: 0}.

Deprecation policy

None. Every field on this page is current as of this release. The service has no deprecated or removed config fields to migrate away from.

Full example

The block below is the shipped example template. The per-section reference below it is authoritative; the example exists for orientation.

Click to expand the shipped example
# Orgmanager Configuration Example
#
# The Organization Manager (orgmanager) provides a REST API to manage IP CIDR
# blocks within an organization. This file documents all available configuration
# options with example values.

# HTTP server configuration
http:
  # Address to listen on (host:port or :port)
  # Leave empty to disable the HTTP server
  address: ":8080"

  # TLS configuration (optional)
  # Uses github.com/PowerDNS/go-tlsconfig
  tls:
    # Path to CA certificate file for client verification
    # ca_file: /etc/ssl/certs/ca.pem

    # Path to server certificate file
    # cert_file: /etc/ssl/certs/server.pem

    # Path to server private key file
    # key_file: /etc/ssl/private/server-key.pem

# Database configuration
database:
  # PostgreSQL connection string
  # Supports environment variable expansion: ${VAR_NAME}
  # Format: postgres://user:password@host:port/database?sslmode=disable
  dsn: "postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:5432/orgmanager?sslmode=disable"

  # For development/testing with SQLite (prefix with "sqlite:" to auto-detect driver):
  # dsn: "sqlite:file:orgmanager.db?cache=shared&_fk=1"
  # Or in-memory:
  # dsn: "sqlite:file::memory:?cache=shared&_fk=1"

# NATS configuration for publishing range updates to ipmapper
nats:
  # Timeout for each request/reply call to ipmapper.
  # Timed-out deployments are retried by the deploy worker.
  request_timeout: 10s

  itsy:
    # NATS server URL
    # Leave url empty to disable NATS publishing
    url: "nats://localhost:4222"

    # Subject prefix for NATS messages
    # Messages are published to: {prefix}.ipmapper.{command}
    # Must match the ipmapper's itsy prefix so that the orgmanager
    # publishes to the same subjects the ipmapper subscribes to.
    prefix: "pdnspf"

    # TLS configuration for NATS connection (optional)
    tls:
      # ca_file: /etc/ssl/certs/nats-ca.pem
      # cert_file: /etc/ssl/certs/nats-client.pem
      # key_file: /etc/ssl/private/nats-client-key.pem

# Asynchronous deployment worker pool configuration.
# Write operations (prefix changes, override upserts) commit to the database and
# return immediately. Deployment to ipmapper happens asynchronously via a pool of
# worker goroutines. Multiple writes during a deployment are coalesced into one
# follow-up deploy, reducing NATS round-trips under load.
deploy:
  # Number of concurrent deployment worker goroutines.
  # Each worker processes one org at a time. Increase if you have many orgs
  # that change concurrently.
  workers: 4

  # How often to check all organization versions against the database.
  # Detects remote changes (e.g. Django admin) and triggers redeployment.
  # On startup, an immediate check runs to discover and deploy all existing orgs.
  # Set to 0 to disable periodic version checking.
  # Format: Go duration string (e.g., "60s", "1m", "5m")
  version_check_interval: 60s

  # Interval for periodically redeploying all known orgs to ipmapper.
  # Set to 0 to disable. Useful as a safety net to ensure ipmapper stays in sync.
  # Format: Go duration string (e.g., "1h", "30m")
  full_redeploy_interval: 0

# Logging configuration
log:
  # Log level: debug, info, warn, error
  level: info

  # Log format: human (colored, for terminals), logfmt (structured), json
  format: human

  # Use UTC timestamps (recommended for production)
  utc: true

http

REST API listener plus the status page, the Prometheus /metrics scrape endpoint, and /health. Leaving address empty disables the listener entirely — useful in odd test setups, but a deployed instance always has it set.

Parameter Type Default Description
address string "" (disabled) Bind address in host:port form (e.g. :8080, 127.0.0.1:8080). Leave empty to disable the HTTP server.
tls tlsconfig.Config {} PowerDNS go-tlsconfig block. Leave as {} for plain HTTP; populate to enable HTTPS.

database

PostgreSQL connection. Required — the service refuses to start with an empty DSN. SQLite is supported for dev and tests via a sqlite: prefix, but the production RPM does not ship the SQLite driver — see SQLite is dev-only below.

Parameter Type Default Description
dsn string "" (required) PostgreSQL connection string. Supports ${VAR} env-var expansion for secrets. Masked in the startup config dump.

Connecting to PostgreSQL

The DSN accepts the standard postgres:// URL form. Common shapes:

  • Plain TCP, password from systemd env:
database:
  dsn: "postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:5432/orgmanager?sslmode=disable"
  • Verified TLS to a managed PostgreSQL:
database:
  dsn: "postgres://orgmanager@db.internal:5432/orgmanager?sslmode=verify-full&sslrootcert=/etc/pki/tls/certs/db-ca.pem"
  • Local Unix socket (no password needed when peer auth is on):
database:
  dsn: "postgres:///orgmanager?host=/var/run/postgresql"
  • Read-write replica with failover uses the standard target_session_attrs=read-write plus a comma-separated host list:
database:
  dsn: "postgres://orgmanager@db-1,db-2,db-3:5432/orgmanager?sslmode=verify-full&target_session_attrs=read-write"

Env-var expansion applies to the whole DSN, so the recommended pattern is to keep the user, host, and port literal in the YAML and pass the password through a systemd drop-in:

# /etc/systemd/system/pdns-pf-orgmanager.service.d/db.conf
[Service]
Environment=DB_PASSWORD=...

SQLite is dev-only

The example file shows a sqlite: DSN for convenience when running the binary out of the source tree. The production RPM is built without CGO support, so the SQLite driver is not linked in. Configuring a sqlite: DSN on a deployed box will fail at startup with a message that names the missing CGO build. Operators on deployed boxes should ignore the SQLite line in the example and keep the DSN pointed at PostgreSQL.

nats

NATS transport for publishing session updates and cleanup commands to the IP Mapper. Leaving itsy.url empty disables publishing — the service still serves the REST API and writes to PostgreSQL, which is occasionally useful for staging or for running the API without an IP Mapper attached.

Parameter Type Default Description
request_timeout go: Duration 10s Timeout for each request/reply call to the IP Mapper. A timed-out deploy request fails the current attempt and is retried by the deploy worker.
itsy itsy.Config {} NATS connection, subject prefix, and TLS. See the common configuration reference for every field.

itsy.prefix must match the IP Mapper

Subjects on the wire are {itsy.prefix}.ipmapper.{command}. The IP Mapper subscribes on the same convention and only sees messages on its configured prefix, so a mismatch is silent — orgmanager will publish, the IP Mapper will not see anything, and only the absence of expected sessions will tip operators off. Set itsy.prefix to the value the receiving IP Mapper is configured with.

Disabling NATS

nats:
  itsy:
    url: ""

The service logs NATS publishing disabled at startup. Writes still commit to PostgreSQL and the deploy queue still drains, but nothing is sent over the wire.

deploy

The asynchronous deployment worker pool plus the two periodic timers. See Architecture → Async deployment to the IP Mapper for the full picture; this section covers the knobs.

Parameter Type Default Description
workers int 4 Number of concurrent deployment workers. Each worker processes one organisation at a time.
pending_queue_size int 10000 Number of organisation IDs that can wait for deploy workers. The default absorbs startup/version-scan and bulk admin bursts while still bounding memory if workers cannot drain the queue.
version_check_interval go: Duration 60s How often to walk every organisation's org_version and queue redeploys for drifted, newly-discovered, or disappeared organisations. 0 disables the periodic scan. The startup scan still runs even at 0.
full_redeploy_interval go: Duration 0 (disabled) How often to queue a full redeploy of every cached organisation as a safety net. 0 disables it.

Sizing workers

The worker pool is the limit on parallel deploys, not on REST throughput — REST writes commit to PostgreSQL and return immediately, they do not wait for a worker. Bump workers if many organisations change at the same time and the deploy queue runs deep, which the status page and the orgmanager_deploy_queue_size metric both expose.

Tuning version_check_interval

The scan is a single SELECT org_id, version FROM organizations followed by an in-memory diff. The cost is dominated by the PostgreSQL round-trip, not the workload. For typical deployments the default 60 seconds is fine; lowering it tightens the worst-case latency between an out-of-band write (admin portal, second orgmanager) and the IP Mapper seeing it.

Setting version_check_interval: 0 disables the periodic scan but not the startup scan — every restart runs one initial pass to populate the in-memory state. Disable the periodic scan only if all writes flow through this orgmanager instance.

Tuning full_redeploy_interval

Periodic full redeploy exists as a safety net — the diffing path already keeps the IP Mapper in sync. Hours are a typical setting (1h, 4h); minutes are usually too aggressive and produce unnecessary NATS traffic. Leave it at 0 unless you have a specific reason to enable it; the IP Mapper has its own paths for detecting drift.

log

Log level and format. See Common configuration → log for every field. The CLI flag --debug overrides log.level to debug.