A lightweight certificate manager that automates ACME HTTP-01 challenges and renewals for groups of domains. It loads configuration from JSON files, provisions or refreshes certificates, and watches for changes to your domains list to update certificates on the fly. Optional S3-backed storage lets you centralize certificate material; otherwise certificates are stored locally.
This program keeps ACME certificates up to date and prefers caching (locally, S3) to request another new certificate so as to not hit rate limits from certificate issuers.
- At startup:
- Reads
config.jsonanddomains.json(defaults under~/.loadmaster). - Ensures the local certificate directory exists.
- Selects storage:
- S3-backed if
s3.bucketNameis set inconfig.json. - Local storage otherwise.
- S3-backed if
- For each domain group in
domains.json, callsUpdateTLSto retrieve from cache and refresh if expiring; falls back to self-signed only if cache is missing.-
A "domain group" is a collection of domains that share the same certificate. (e.g.,
example.com,www.example.com,mail.example.com)
-
- Reads
- Long-running process:
- Watches
domains.jsonfor changes and re-runsUpdateTLSfor each group on write/create. - Also triggers a refresh loop every 24 hours, upgrading certs that are close to expiring.
- Watches
ACME HTTP-01 challenges are served on a configurable port (default: 5002). You should proxy /.well-known/acme-challenge/* requests to this port from your public HTTP endpoint.
By default, the app looks in ~/.loadmaster for config.json and domains.json. If either file is missing, it will create a default version, print a message to edit the file, and exit.
- Default directory:
~/.loadmaster(config.DefaultConfigDir) - Default paths:
~/.loadmaster/config.json~/.loadmaster/domains.json
- Local certificate directory:
- Defaults to
~/.loadmaster/certsunless overridden internally. - Created automatically if it does not exist.
- Defaults to
Fields:
email(string): Contact email used for ACME registration.caAuthority(string): ACME CA directory URL. Defaults to Let’s Encrypt staging:https://acme-staging-v02.api.letsencrypt.org/directory.s3(object): Optional S3 settings for remote storage.bucketName(string): If set, S3 storage is used.endpoint(string): Custom S3-compatible endpoint (optional).region(string): AWS region for the bucket.
Example:
{
"email": "[email protected]",
"caAuthority": "https://acme-staging-v02.api.letsencrypt.org/directory",
"s3": {
"bucketName": "my-certificates",
"endpoint": "",
"region": "us-east-1"
}
}
Notes:
- Use the production Let’s Encrypt directory when you’re ready:
https://acme-v02.api.letsencrypt.org/directory. - When
s3.bucketNameis non-empty, the app constructs S3 storage with:BucketName,ContactEmail,LocalCertDir,CAAuthority
- Otherwise, local storage is used via
acme.NewLocalACMEStorage(email, caAuthority).
Fields:
domains(array of arrays of strings): Each inner array is a domain group that will share a certificate (e.g., primary domain plus its aliases).
Example:
{
"domains": [
["example.com", "www.example.com"],
["api.example.com", "api.internal.example.com"]
]
}
Notes:
- On startup and on any change to
domains.json, each group is processed viastorage.UpdateTLS(group).
To build the binary, run:
go build -o loadmaster main.goYou can always run with
go run .
You can run the binary with optional flags to point at config files and set the ACME challenge port.
Flags:
-domains(string): Path todomains.json. Default:~/.loadmaster/domains.json.-config(string): Path toconfig.json. Default:~/.loadmaster/config.json.-port(int): Port to serve ACME HTTP-01 challenges. Default:5002.
Example:
./loadmaster \
-config "$HOME/.loadmaster/config.json" \
-domains "$HOME/.loadmaster/domains.json" \
-port 5002
Behavior:
- Logs startup info and file paths.
- Ensures
LocalCertDirexists (default~/.loadmaster/certs). - Loads domains and processes each group.
- Watches
domains.jsonfor writes/creates with a short delay to ensure complete writes. - Every 24 hours, triggers a refresh pass for all domain groups.
server {
listen 80;
server_name example.com www.example.com;
# Proxy ACME HTTP-01 challenge requests to the cert manager
location ^~ /.well-known/acme-challenge/ {
proxy_pass http://127.0.0.1:5002;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_read_timeout 30s;
}GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007
