Configuration

Every PACKYARD_* environment variable, what it does, what defaults look like, and which ones the server refuses to start without.

Updated April 27, 2026

All Packyard configuration is via PACKYARD_* environment variables. The interactive wizard (packyard init) writes them to an env file (default /etc/packyard/packyard.env) that the systemd unit reads on boot. You can also export them in a shell, set them in a Docker --env-file, or pass them into a Kubernetes Deployment.

The server refuses to start if a required value is missing — there's no silent default for anything security-relevant.

Server

Variable Default Purpose
PACKYARD_PORT 9090 HTTP listen port.
PACKYARD_BASE_URL (required) Canonical public URL (e.g. https://repo.example.com). Embedded in Composer dist URLs — Composer clients fetch zip downloads from this hostname, so it must match the public-facing URL exactly.
PACKYARD_LOG_LEVEL info One of debug, info, warn, error.

Database

PACKYARD_DB_DRIVER=sqlite     # sqlite | mysql | postgres
PACKYARD_DB_HOST=localhost
PACKYARD_DB_PORT=3306
PACKYARD_DB_NAME=packyard
PACKYARD_DB_USER=packyard
PACKYARD_DB_PASSWORD=
PACKYARD_DB_SSLMODE=disable
PACKYARD_DB_PATH=./packyard.db    # SQLite only
Variable Default Purpose
PACKYARD_DB_DRIVER sqlite Pick sqlite for a single host, mysql or postgres when you need a managed database (e.g. multi-instance deploys, separate DB host).
PACKYARD_DB_PATH ./packyard.db SQLite file path (relative to the working directory or absolute). Ignored for mysql / postgres.
PACKYARD_DB_HOST / _PORT / _NAME / _USER / _PASSWORD — Connection details for mysql / postgres.
PACKYARD_DB_SSLMODE disable Postgres SSL mode (disable, require, verify-full, etc.).

Migrations run automatically on startup against the configured driver. Run them ahead of time with packyard migrate if you'd rather not have a first request stall.

Storage

PACKYARD_STORAGE_TYPE=local       # local | s3
PACKYARD_STORAGE_LOCAL_PATH=./data/packages
Variable Default Purpose
PACKYARD_STORAGE_TYPE local local keeps zips on disk; s3 puts them in any S3-compatible bucket (AWS, Cloudflare R2, MinIO, Backblaze B2).
PACKYARD_STORAGE_LOCAL_PATH ./data/packages Directory for local storage.

For s3:

PACKYARD_S3_BUCKET=packyard-packages
PACKYARD_S3_REGION=us-east-1
PACKYARD_S3_ENDPOINT=                 # e.g. https://s3.eu-central-003.backblazeb2.com
PACKYARD_S3_ACCESS_KEY=
PACKYARD_S3_SECRET_KEY=
PACKYARD_S3_FORCE_PATH_STYLE=false    # true for MinIO, R2, Backblaze

Use path-style addressing (PACKYARD_S3_FORCE_PATH_STYLE=true) for non-AWS providers that don't support virtual-host buckets.

Auth and sessions

Variable Default Purpose
PACKYARD_SESSION_SECRET (required) 32+ char random string used to sign session cookies (HMAC-SHA256). The server refuses to start if this is shorter than 32 characters. Generate with openssl rand -hex 32.
PACKYARD_SESSION_MAX_AGE 86400 Session lifetime in seconds (default: 24 h).
PACKYARD_COOKIE_DOMAIN empty Session-cookie Domain attribute. Empty scopes the cookie to the exact hostname (correct when the dashboard and Composer endpoint share a host). Set to a parent domain like .example.com to share login state across subdomains.
PACKYARD_COOKIE_SAMESITE strict strict (safest), lax, or none. Use lax if you've set PACKYARD_COOKIE_DOMAIN and need the cookie to survive cross-subdomain navigations. none requires HTTPS.
PACKYARD_TRUSTED_PROXIES empty Comma-separated CIDRs (or bare IPs) of reverse proxies whose X-Forwarded-For header is honored. Leave empty when the server is exposed directly — otherwise the per-IP rate limiter can be bypassed by header injection. Example: 10.0.0.0/8,127.0.0.1.
PACKYARD_BCRYPT_COST 12 bcrypt cost factor for password hashing.

Performance and retention

Variable Default Purpose
PACKYARD_DOWNLOAD_RETENTION_DAYS 90 A daily goroutine prunes download_events rows older than this. 0 keeps events forever (useful for audit trails; the table grows without bound). Per-version cumulative counters (download_count) are never affected — they persist regardless.
PACKYARD_DIST_RATELIMIT 120 Requests allowed per PACKYARD_DIST_RATELIMIT_WINDOW_SECONDS per client IP on the Composer /dist/ endpoint. Caps how fast a compromised token can inflate counters. Defaults accommodate normal composer install bursts.
PACKYARD_DIST_RATELIMIT_WINDOW_SECONDS 60 Rate-limit window.
PACKYARD_STATS_CACHE_TTL_SECONDS 60 How long /packages/stats responses stay cached in memory, per-org. Six DB queries per request collapse into one per TTL window. 0 disables caching.
PACKYARD_SYNC_WORKERS 2 Number of goroutines pulling sync jobs from the queue. 0 disables the worker pool entirely (useful for read-only replicas).
PACKYARD_JOB_RETENTION_DAYS 30 Daily prune of finished sync_jobs rows. Active (queued/running) jobs are never touched. 0 disables pruning.

Git providers

Optional. Used when the dashboard syncs packages from a Git host's release pages.

PACKYARD_GITHUB_TOKEN=      # GitHub personal access token
PACKYARD_GITLAB_TOKEN=      # GitLab personal access token

Public repositories work without a token but are subject to lower rate limits.

Initial admin (first-run only)

PACKYARD_ADMIN_EMAIL=[email protected]
PACKYARD_ADMIN_PASSWORD=changeme

Used once on first boot to create the super-admin user. Subsequent runs ignore these values — change the admin password from the dashboard or with packyard admin user reset-password.

Verifying your config

Without starting the server:

packyard check-config       # validates the env file
packyard check-db           # confirms the DB is reachable
packyard healthcheck        # probes /healthz on the configured port

check-config is the one to run first after editing the env file — it surfaces missing required values, malformed durations, and bad SSL modes before you go to start the service.