Configuration Reference
Complete reference for all YAML configuration sections
ACMEEH uses a single YAML configuration file validated against a JSON Schema. The file is loaded via ConfigKit, which provides schema validation, environment variable substitution, and typed access.
Environment Variables
Use ${VAR} or ${VAR:-default} syntax anywhere in the YAML to reference environment variables:
database:
password: ${DB_PASSWORD}
host: ${DB_HOST:-localhost}
Variables are resolved during config loading in additional_checks(). If a variable is missing and has no default, config loading fails with a clear error.
Tip
Inline Comments
Add a _comment field (string or array of strings) at the top level of your YAML for in-file documentation. It is ignored by the server.
Settings Sections
The configuration tree has 27 top-level sections. Only server.external_url, database.database, and database.user are required — everything else has sensible defaults.
server
HTTP server settings for both development and gunicorn production modes.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
required |
Public-facing URL of the server (used in ACME directory) |
|
string |
|
Address to bind to |
|
integer |
|
Port to listen on |
|
integer |
|
Number of gunicorn workers |
|
string |
|
Gunicorn worker class |
|
integer |
|
Worker timeout in seconds |
|
integer |
|
Graceful shutdown timeout |
|
integer |
|
Keep-alive timeout |
|
integer |
|
Max requests before worker restart (0 = disabled) |
|
integer |
|
Random jitter added to max_requests |
proxy
Reverse proxy configuration for extracting real client IP and protocol.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable reverse proxy support |
|
string[] |
|
List of trusted proxy IP addresses/ranges |
|
string |
|
Header containing real client IP |
|
string |
|
Header containing original protocol |
Note
X-Forwarded-Prefix
The middleware also handles X-Forwarded-Prefix automatically (not configurable). When present on a trusted request, its value is used as the WSGI SCRIPT_NAME to support reverse proxies that mount ACMEEH under a sub-path.
security
Cryptographic policies, rate limiting, and identifier restrictions.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string[] |
|
Allowed JWS signing algorithms |
|
integer |
|
Minimum RSA key size for account keys |
|
integer |
|
Maximum RSA key size |
|
string[] |
|
Allowed EC curves for account keys |
|
integer |
|
Maximum request body size |
|
string[] |
|
Allowed CSR signature algorithms |
|
integer |
|
Minimum RSA key size in CSRs |
|
integer |
|
Minimum EC key size in CSRs |
|
integer |
|
HSTS header max-age in seconds |
|
boolean |
|
Require every account to have a CSR profile assigned before certificate finalization. See CSR Profile Enforcement. |
security.rate_limits
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable rate limiting |
|
string |
|
Rate limit storage backend |
|
object |
|
Nonce endpoint rate limit |
|
object |
|
Account creation rate limit |
|
object |
|
Order creation rate limit |
|
object |
|
Per-identifier order rate limit |
|
object |
|
Challenge response rate limit |
|
object |
|
Challenge validation rate limit |
|
integer |
|
Rate limit GC interval |
|
integer |
|
Max age for rate limit entries |
Each rate limit rule has requests (count) and window_seconds (time window).
security.identifier_policy
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string[] |
|
Allowlist of domain suffixes (empty = allow all) |
|
string[] |
|
Blocklist of domain suffixes |
|
boolean |
|
Allow wildcard identifiers |
|
boolean |
|
Allow IP address identifiers |
|
integer |
|
Maximum identifiers per order |
|
integer |
|
Maximum identifier string length |
|
boolean |
|
Enforce per-account allowed identifiers |
acme
ACME protocol settings including URL paths and policy.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
URL for terms of service / website |
|
string[] |
|
CAA record identities for validation |
|
boolean |
|
Require External Account Binding |
|
boolean |
|
Allow EAB credentials to be reused across multiple accounts |
|
boolean |
|
Enforce CAA DNS record checks |
|
integer |
|
Number of orders per page in account orders list |
acme.paths
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Directory endpoint path |
|
string |
|
New nonce endpoint path |
|
string |
|
New account endpoint path |
|
string |
|
New order endpoint path |
|
string |
|
Pre-authorization endpoint path |
|
string |
|
Certificate revocation endpoint path |
|
string |
|
Key change endpoint path |
api
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Base URL path prefix for all ACME endpoints |
challenges
Challenge validation configuration.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string[] |
|
Enabled challenge types: |
|
boolean |
|
Automatically accept all challenges without validation |
|
integer |
|
Retry-After header value for pending challenges |
|
integer |
|
Base delay for exponential backoff on retries |
|
integer |
|
Maximum backoff delay |
challenges.http01
Field |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
|
Port to connect to for HTTP-01 validation |
|
integer |
|
Connection timeout |
|
integer |
|
Maximum validation retries |
|
boolean |
|
Auto-validate this challenge type |
|
string[] |
|
Networks blocked from validation (SSRF protection) |
|
integer |
|
Maximum response body size for HTTP-01 validation (1 MB) |
challenges.dns01
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string[] |
|
DNS resolver addresses (empty = system default) |
|
integer |
|
DNS query timeout |
|
integer |
|
Wait time for DNS propagation |
|
integer |
|
Maximum validation retries |
|
boolean |
|
Auto-validate this challenge type |
|
boolean |
|
Require DNSSEC validation |
|
boolean |
|
Require response from authoritative nameserver |
challenges.tlsalpn01
Field |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
|
Port for TLS-ALPN-01 validation |
|
integer |
|
Connection timeout |
|
integer |
|
Maximum validation retries |
|
boolean |
|
Auto-validate this challenge type |
challenges.background_worker
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable background challenge worker |
|
integer |
|
Polling interval |
|
integer |
|
Threshold for stale challenges |
ca
Certificate Authority settings. See CA Backends for detailed backend configuration.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
CA backend: |
|
integer |
|
Default certificate validity |
|
integer |
|
Maximum certificate validity |
|
integer |
|
Failures before circuit opens |
|
float |
|
Seconds before circuit half-opens |
|
integer |
|
Timeout in seconds for deferred (background thread) signing. Used by backends where |
ca.profiles
Named certificate profiles. A default profile is always present. Each profile supports:
Field |
Type |
Default |
Description |
|---|---|---|---|
|
array |
|
Key Usage extension values (e.g., |
|
array |
|
Extended Key Usage values (e.g., |
|
integer |
|
Override |
|
integer |
|
Override |
Example:
ca:
profiles:
default:
key_usages: [digital_signature, key_encipherment]
extended_key_usages: [server_auth]
client:
key_usages: [digital_signature]
extended_key_usages: [client_auth]
validity_days: 365
max_validity_days: 730
database
PostgreSQL connection settings.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Database hostname |
|
integer |
|
Database port |
|
string |
required |
Database name |
|
string |
required |
Database user |
|
string |
|
Database password |
|
string |
|
PostgreSQL SSL mode |
|
integer |
|
Minimum pool connections |
|
integer |
|
Maximum pool connections |
|
float |
|
Connection timeout in seconds |
|
float |
|
Maximum time (seconds) a connection can remain idle in the pool before being closed |
|
boolean |
|
Automatically create tables on startup |
dns
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string[] |
|
DNS resolver addresses |
|
integer |
|
DNS query timeout |
|
integer |
|
DNS query retries |
email
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Require email contact on accounts |
|
string[] |
|
Allowed email domains (empty = any) |
|
boolean |
|
Validate MX records for email domain |
account
Account modification and lifecycle policy settings.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Allow accounts to update their contact information |
|
boolean |
|
Allow accounts to self-deactivate |
smtp
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable SMTP for notifications |
|
string |
|
SMTP server hostname |
|
integer |
|
SMTP server port |
|
string |
|
SMTP username |
|
string |
|
SMTP password |
|
boolean |
|
Use STARTTLS |
|
string |
|
Sender email address |
|
list[string] |
|
CC recipients added to every outgoing notification |
|
list[string] |
|
BCC recipients added to every outgoing notification (envelope only, not in headers) |
|
string |
|
Custom Jinja2 templates directory |
|
integer |
|
SMTP operation timeout |
logging
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Log level: DEBUG, INFO, WARNING, ERROR |
|
string |
|
Log format: |
logging.audit
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable audit logging |
|
string |
|
Audit log file path |
|
boolean |
|
Send audit logs to syslog |
|
integer |
|
Maximum audit log file size before rotation (100 MB) |
|
integer |
|
Number of rotated audit log backups to keep |
notifications
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable notification system |
|
integer |
|
Max delivery retries |
|
integer |
|
Initial retry delay |
|
integer |
|
Notifications per batch |
|
integer |
|
Retry worker interval |
|
integer[] |
|
Days before expiry to warn |
|
integer |
|
Expiration check interval |
|
float |
|
Exponential backoff multiplier |
|
integer |
|
Maximum retry delay |
|
string[] |
|
Notification types to suppress. Valid values:
|
hooks
Lifecycle hook configuration. See Hook System in the Development guide for writing custom hooks.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
|
Default hook execution timeout |
|
integer |
|
Thread pool size for hook execution |
|
integer |
|
Default retry count on failure |
|
string |
|
File path for failed hook payloads |
hooks.registered[]
See Extensibility for the full list of hook events, configuration fields, and a complete YAML example.
nonce
Field |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
|
Nonce expiry time |
|
integer |
|
Garbage collection interval |
|
integer |
|
Nonce byte length |
|
boolean |
|
Log consumed nonces in audit trail |
|
integer |
|
Max age for nonce reuse. Defaults to the lesser of |
order
Field |
Type |
Default |
Description |
|---|---|---|---|
|
integer |
|
Order expiry (7 days) |
|
integer |
|
Authorization expiry (30 days) |
|
integer |
|
Expired order cleanup interval |
|
integer |
|
Threshold for stale processing orders |
|
integer |
|
Pre-authorization validity |
|
integer |
|
Retry-After for processing orders |
quotas
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable per-account quotas |
|
integer |
|
Daily cert limit (0 = unlimited) |
|
integer |
|
Daily order limit (0 = unlimited) |
tos
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Terms of Service URL |
|
boolean |
|
Require TOS agreement for account creation |
admin_api
See Admin API for endpoint documentation.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable admin REST API |
|
string |
|
Admin API base path |
|
string |
|
JWT signing secret |
|
integer |
|
Token validity period |
|
string |
|
Email for auto-created initial admin |
|
integer |
|
Generated password length |
|
integer |
|
Default pagination size |
|
integer |
|
Maximum pagination size |
crl
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable CRL distribution point |
|
string |
|
CRL endpoint path |
|
integer |
|
Automatic rebuild interval |
|
integer |
|
Next Update field in CRL |
|
string |
|
CRL signing hash algorithm |
metrics
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable Prometheus metrics endpoint |
|
string |
|
Metrics endpoint path |
|
boolean |
|
Require authentication for metrics |
ct_logging
Certificate Transparency log submission.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable CT log submission |
|
boolean |
|
Submit pre-certificates |
ct_logging.logs[]
Each entry in the logs array defines a Certificate Transparency log to submit to:
ct_logging:
enabled: true
logs:
- url: https://ct.example.com/log
public_key_path: /path/to/ct-log-key.pem
timeout_seconds: 10
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
required |
CT log submission URL |
|
string |
|
Path to the CT log’s public key (PEM). Used for SCT verification. |
|
integer |
|
HTTP request timeout for CT log submission |
audit_retention
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable audit log cleanup |
|
integer |
|
Maximum audit log age |
|
integer |
|
Cleanup interval |
ari
ACME Renewal Information (draft-ietf-acme-ari).
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable ARI endpoint |
|
float |
|
Suggested renewal point (fraction of validity) |
|
string |
|
ARI endpoint path |
audit_export
Field |
Type |
Default |
Description |
|---|---|---|---|
|
string |
|
Webhook URL for audit event export |
|
string |
|
Syslog server hostname |
|
integer |
|
Syslog server port |
retention
Data retention policies for automatic cleanup of expired/invalid resources.
Field |
Type |
Default |
Description |
|---|---|---|---|
|
boolean |
|
Enable data retention cleanup |
|
integer |
|
Max age for invalid orders |
|
integer |
|
Max age for expired authorizations |
|
integer |
|
Max age for invalid challenges |
|
integer |
|
Max age for expiration notices |
|
integer |
|
Cleanup worker interval |
|
integer |
|
Internal cleanup loop sleep interval |
Cross-Field Validation Rules
ACMEEH performs cross-field validation during config loading. Errors prevent startup; warnings are logged but non-fatal.
Errors (prevent startup)
Condition |
Message |
|---|---|
|
External URL must not end with a slash |
|
Required paths for internal CA backend |
|
Required fields for ACME proxy backend |
|
Required fields for HSM backend |
|
PIN required when login is required |
|
Default validity must not exceed maximum |
|
Min connections must not exceed max |
|
Backoff base must not exceed max |
|
SMTP host and from_address required when enabled |
|
TOS URL required when agreement is required |
|
Trusted proxies must be specified when enabled |
|
Token secret and initial admin email required |
|
Admin and ACME paths must not overlap |
|
At least one CT log required when enabled |
|
Minimum 16 bytes for cryptographic safety |
|
Minimum RSA key size is 2048 bits |
Unknown hook event name in |
Event must be a known lifecycle event |
Invalid hook class path format |
Must be a valid dotted Python path (e.g., |
Warnings (logged, non-fatal)
Condition |
Message |
|---|---|
|
Notifications will be recorded but not sent via email |
|
Generated passwords will only be printed to stdout/log |
|
Cannot manage allowlists without admin API |
|
CSR profiles cannot be managed without admin API |
|
EAB reusability has no effect when EAB is not required |
|
In-memory rate limits are per-process, not shared across workers |
|
Connection pool may be too small for configured workers |
|
MX validation will use system DNS resolvers |
|
Quotas are enabled but have no effect |
0 < |
HSTS max-age less than 1 day is not recommended |
|
CRL generation requires access to the CA signing key |
Hook |
Hook execution may outlive the HTTP request |
CSR Profile Enforcement
When security.require_csr_profile is true, every ACME account must have a
CSR profile assigned before it can finalize an order. If an account has no profile,
the finalize request is rejected with a badCSR error.
This pairs naturally with EAB-based enrollment: assign a CSR profile to each EAB credential, and when a client registers using that credential, the profile is automatically copied to the new account.
Recommended setup:
acme:
eab_required: true
security:
require_csr_profile: true
admin_api:
enabled: true
token_secret: ${ADMIN_TOKEN_SECRET}
initial_admin_email: admin@example.com
base_path: /admin
Workflow:
An admin creates a CSR profile via
POST /admin/csr-profilesThe admin assigns it to an EAB credential via
PUT /admin/eab/{eab_id}/csr-profile/{profile_id}A client registers with the EAB credential — the profile is copied to the new account
All certificate orders from that account are validated against the profile
Accounts without a profile receive a badCSR error at finalization with the detail
“No CSR profile assigned to this account”.
Note
The admin_api.enabled option must be true for CSR profile management. If
require_csr_profile is true but the admin API is disabled, a configuration
warning is logged at startup and all finalization requests will fail.
Full Configuration Example
A comprehensive configuration file showing all commonly used sections:
# ACMEEH Configuration --- Full Example
server:
external_url: https://acme.example.com
bind: 0.0.0.0
port: 8443
workers: 8
timeout: 30
max_requests: 1000
max_requests_jitter: 50
proxy:
enabled: true
trusted_proxies:
- 10.0.0.0/8
- 172.16.0.0/12
database:
host: ${DB_HOST:-localhost}
port: 5432
database: acmeeh
user: acmeeh
password: ${DB_PASSWORD}
sslmode: require
max_connections: 20
auto_setup: true
ca:
backend: internal
default_validity_days: 90
max_validity_days: 397
internal:
root_cert_path: /etc/acmeeh/ca/root-ca.pem
root_key_path: /etc/acmeeh/ca/root-ca-key.pem
profiles:
default:
key_usages: [digital_signature, key_encipherment]
extended_key_usages: [server_auth]
client:
key_usages: [digital_signature]
extended_key_usages: [client_auth]
validity_days: 365
challenges:
enabled:
- http-01
- dns-01
dns01:
resolvers:
- 8.8.8.8
- 8.8.4.4
propagation_wait_seconds: 30
security:
allowed_algorithms: [ES256, RS256]
rate_limits:
enabled: true
identifier_policy:
allowed_domains:
- .example.com
- .internal.corp
allow_wildcards: true
acme:
eab_required: true
caa_enforce: true
caa_identities:
- acme.example.com
tos:
url: https://example.com/tos
require_agreement: true
admin_api:
enabled: true
token_secret: ${ADMIN_TOKEN_SECRET}
initial_admin_email: admin@example.com
smtp:
enabled: true
host: smtp.example.com
port: 587
username: ${SMTP_USER}
password: ${SMTP_PASSWORD}
from_address: acmeeh@example.com
cc: []
bcc: []
notifications:
enabled: true
expiration_warning_days: [30, 14, 7, 1]
logging:
level: INFO
format: json
audit:
enabled: true
file: /var/log/acmeeh/audit.log
crl:
enabled: true
metrics:
enabled: true