README
¶
loadmaster
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.
tl;dr
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.
How it works
- 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.
Configuration
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
config.json
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": "admin@example.com",
"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).
domains.json
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).
Building
To build the binary, run:
go build -o loadmaster main.go
You can always run with
go run .
Running
You can run the binary with optional flags to point at config files and set the ACME challenge port.
Flags:
-data(string): Base data directory for config, domains, certs, and ACME user data. Default:~/.loadmaster.-domains(string): Path todomains.json. Default:<data>/domains.json.-config(string): Path toconfig.json. Default:<data>/config.json.-certs(string): Path to certificates directory. Default:<data>/certs.-port(int): Port to serve ACME HTTP-01 challenges. Default:5002.
Example:
./loadmaster \
-data "$HOME/.loadmaster" \
-port 5002
Individual paths can still be overridden:
./loadmaster \
-data /var/lib/loadmaster \
-certs /etc/ssl/loadmaster \
-port 5002
Behavior:
- Logs startup info and file paths.
- Ensures the certs directory exists (default
<data>/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.
Running with Docker
Building the Docker image
You can build the Docker image using the provided Dockerfile:
docker build -t loadmaster:latest .
Running with Docker
To run loadmaster in a Docker container, mount a single data directory containing your config files:
docker run -d \
--name loadmaster \
-p 5002:5002 \
-v $(pwd)/data:/data \
loadmaster:latest
The container expects the following inside the data directory:
config.json- application configurationdomains.json- domain listcerts/- certificate storage (created automatically)
ACME user registration files are also stored in the data directory.
If you're using S3 for storage, pass AWS credentials as environment variables:
docker run -d \
--name loadmaster \
-p 5002:5002 \
-v $(pwd)/data:/data \
-e AWS_ACCESS_KEY_ID=your_access_key \
-e AWS_SECRET_ACCESS_KEY=your_secret_key \
-e AWS_REGION=us-east-1 \
loadmaster:latest
Running with Docker Compose
A docker-compose.yml file is provided for easier deployment. First, create the necessary directory structure:
mkdir -p data
Create your data/config.json and data/domains.json files (see Configuration section above for examples).
Then start the service:
docker compose up -d
To view logs:
docker compose logs -f loadmaster
To stop the service:
docker compose down
Note: The Docker Compose setup mounts:
./datadirectory to/data(containsconfig.json,domains.json,certs/, and ACME user data)- Port
5002for ACME HTTP-01 challenges
If using S3 storage, add an environment section in docker-compose.yml and provide your AWS credentials.
Example NGINX proxy for ACME challenges
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;
}
License
GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007
Documentation
¶
There is no documentation for this package.