Overview
Snapp configuration is centralized in a single settings.yaml file.
Earlier versions split configuration across environment variables, database state, and partially hard-coded defaults. This made behavior opaque, hard to reason about, and error-prone. The current model intentionally collapses all runtime configuration into a single, validated document.
settings.yaml is now:
- The only source of truth
- Loaded at startup
- Hot-reloaded at runtime
- Strictly validated on every change
- Backed by explicit defaults and fallbacks
The file is monitored via filesystem metadata (mtime). Any valid change is applied live without restarting the process or container.
Configuration file location
By default, Snapp reads /app/config/settings.yaml
In Docker deployments, this is typically mounted from: ./config/settings.yaml
If the file does not exist, Snapp will:
- Generate a minimal fallback configuration
- Start normally
- Persist the generated file to disk
Validation and fallback model
Configuration is parsed using a strict schema (Valibot).
Behavior on load:
- Missing fields are filled using schema defaults
- Invalid values reject the update
- The last valid configuration remains active
- Errors are logged, never silently ignored
This applies both at startup and during live reloads.
Minimal configuration
This is the smallest valid configuration.
appname: Snapp
admin:
- email: admin@example.org
username: admin
hosts:
- origin: https://example.org
This configuration is sufficient to:
- Bootstrap an admin account
- Allow access from a single public origin
- Run Snapp without optional integrations
Full configuration example
The following example represents all supported fields.
appname: Snapp
admin:
- email: admin@example.org
username: admin
hosts:
- origin: https://auth.example.org
options:
customRedirect: /dashboard
disable:
homepage: false
limits: true
lowerCaseFallback: true
signup: false
twoFactor: true
limits:
maxSnappPerUser: 0
requestsPerDay: 100
requestsPerMinute: 14400
thirdparty:
umami:
url: https://metrics.example.org
websiteId: 00000000-0000-0000-0000-000000000000
vtapi:
apikey: REDACTED
- origin: https://example.org
options:
customRedirect: /dashboard
disable:
homepage: false
limits: true
signup: false
twoFactor: true
smtp:
enabled: true
from: no-reply@example.org
host: smtp.example.org
port: 465
secure: true
user: no-reply@example.org
pass: REDACTED
Top-level fields
appname
appname: Snapp
- Display name of the application
- Used in UI, authentication flows, and email templates
- Optional
- Defaults to
Snapp
Admin bootstrap
admin:
- email: admin@example.org
username: admin
Admin entries are reconciled on startup.
Behavior:
- On startup, Snapp ensures that each configured admin exists
- If an admin user is deleted from the UI, it is recreated on next startup
- Passwords are generated once and printed to logs
- Passwords are never regenerated automatically
- Removing this section does not remove existing admins
Fields:
emailRequired, validated email addressusernameRequired, minimum length enforced
Hosts
The hosts array defines all allowed public origins and their runtime behavior.
Each entry represents a logical tenant.
hosts:
- origin: https://example.org
origin
Fully qualified origin (protocol required)
Used for:
- CORS validation
- Redirect validation
- Public link resolution
- Authentication base URL
A slugified version of the origin is used as the organization ID in the database
options
Per-host behavioral configuration.
customRedirect
customRedirect: /dashboard
- Default redirect path when the homepage is disabled
- Applied after authentication
- Defaults to
/dashboard
disable
Feature-level switches.
disable:
homepage: false
limits: true
lowerCaseFallback: true
signup: false
twoFactor: true
homepageDisable the public landing pagelimitsDisable rate limiting and usage limits entirelylowerCaseFallbackDisable automatic lowercase shortcode fallbacksignupDisable public user registrationtwoFactorDisable 2FA enforcement and setup
These flags directly influence authentication, routing, and rate-limiting behavior.
limits
limits:
maxSnappPerUser: 0
requestsPerDay: 100
requestsPerMinute: 14400
maxSnappPerUser-1or negative values = unlimited
requestsPerDay- Per user, per host
requestsPerMinute- Burst protection
Limits are enforced only if disable.limits is false.
Internally, limits influence both API key throttling and authenticated requests.
Third-party integrations
Umami analytics
thirdparty:
umami:
url: https://metrics.example.org
websiteId: 00000000-0000-0000-0000-000000000000
Enables server-side analytics
Used for:
- Visit logging
- Aggregated metrics
Optional
Disabled if omitted
VirusTotal API
thirdparty:
vtapi:
apikey: REDACTED
Enables URL reputation checks
Used during:
- Link creation
- Security audits
Optional
Strongly recommended for public instances
SMTP configuration
SMTP is used for authentication-related emails.
smtp:
enabled: true
from: no-reply@example.org
host: smtp.example.org
port: 465
secure: true
user: no-reply@example.org
pass: REDACTED
Fields:
enabledWhenfalse, emails are rendered to logsfromSender addresshostSMTP server hostnameportUsually465(implicit TLS) or587securetruefor implicit TLSuserpass
SMTP credentials are never stored elsewhere.
Runtime behavior
- Configuration is reloaded on file change
- Invalid updates are rejected
- The last valid configuration remains active
- Authentication clients are refreshed per host when relevant settings change
- No restart required
Notes
settings.yamlis statefulBack it up together with:
- PostgreSQL volume
- Uploaded assets (
avatars,logos)
Secrets are stored in plain text
- Protect the file accordingly
Beta versions may introduce new fields or defaults