Certs Maker
Generate TLS certificates for development and self-hosted services with a single command.
Certs Maker turns a list of DNS names and IP addresses into certificate files you can use with a web server, reverse proxy, or local service. It runs as a one-shot Docker container or a native macOS/Linux program and uses OpenSSL to generate RSA 2048-bit keys and SHA-256 signatures.
- Include multiple domains, wildcard domains, IPv4, and IPv6 addresses in one certificate.
- Export a certificate and private key in both PEM and DER formats.
- Choose a self-signed certificate or a local root CA with a signed server certificate.
- Configure names, validity, and output through environment variables or CLI flags.
Quick start
With Docker installed and running, generate a certificate for lab.com, its direct subdomains, and 127.0.0.1:
docker run --rm -v "$(pwd)/ssl:/ssl" \
-e "CERT_DNS=lab.com,*.lab.com,127.0.0.1" \
soulteary/certs-maker:v3.8.1
The container exits after generation. Your files are saved to ./ssl on the host:
ssl/
├── lab.com.conf
├── lab.com.der.crt
├── lab.com.der.key
├── lab.com.pem.crt
└── lab.com.pem.key
For a server that accepts PEM files, configure:
- Certificate:
ssl/lab.com.pem.crt - Private key:
ssl/lab.com.pem.key
Version note: the published v3.8.1 image supports this basic example, but predates the custom-validity, Firefox SAN, and CLI-precedence fixes on main. Build current source to use those fixes. See release compatibility for details.
Docker Compose
Save the following as docker-compose.yml:
services:
certs-maker:
image: soulteary/certs-maker:v3.8.1
environment:
CERT_DNS: "lab.com,*.lab.com,127.0.0.1"
volumes:
- ./ssl:/ssl
Run it once and remove the container afterward:
docker compose run --rm certs-maker
The bind mount maps host ./ssl to container /ssl. To use another host directory, change the source of the mount, for example ./certificates:/ssl.
Build from source
Current source includes the fixes described above. You need the Go version required by go.mod, currently Go 1.27.2, plus openssl and sh in PATH.
git clone https://github.com/soulteary/certs-maker.git
cd certs-maker
go build -o certs-maker .
./certs-maker "--CERT_DNS=lab.com,*.lab.com,127.0.0.1" --EXPIRE_DAYS=30
This generates a certificate valid for 30 days in ./ssl.
Prebuilt macOS and Linux archives are available from GitHub Releases. Native binaries also require OpenSSL and a shell. The installation guide covers platform selection, checksums, and packaging current source into a local Docker image.
Choose a certificate mode
The following behavior applies to current source. For Firefox mode and custom validity, use a source build until a published release includes the fixes.
| Mode | Setting | Certificate and output |
| --- | --- | --- |
| Default | Both mode flags off | Self-signed certificate with CA:TRUE; five PEM/DER/configuration files. |
| Kubernetes | FOR_K8S=ON | Self-signed server certificate with CA:FALSE; default file prefix gains .k8s. |
| Firefox / local CA | FOR_FIREFOX=ON | Local root CA plus a separately signed server certificate with CA:FALSE and DNS/IP SANs. |
Kubernetes
./certs-maker "--CERT_DNS=lab.com,*.lab.com,127.0.0.1" \
--FOR_K8S=ON --DIR=ssl-k8s
Use ssl-k8s/lab.com.k8s.pem.crt and ssl-k8s/lab.com.k8s.pem.key for your server or TLS Secret. This mode also adds localhost and SAN entries. The entry does not make the certificate valid for every hostname: always include the actual DNS names and IP addresses clients use.
You create and configure the Kubernetes Secret or Ingress yourself. See the Kubernetes usage example.
Firefox / local CA
./certs-maker "--CERT_DNS=lab.com,*.lab.com,127.0.0.1" \
--FOR_FIREFOX=ON --EXPIRE_DAYS=30 --DIR=ssl-firefox
Along with the ordinary five files, this creates .rootCA.pem, .rootCA.key, .pem.csr, and .rootCA.srl files.
- Server: deploy
ssl-firefox/lab.com.pem.crtandssl-firefox/lab.com.pem.key. - Clients: trust the public
ssl-firefox/lab.com.rootCA.pemcertificate. - Private material: keep both
.pem.keyand.rootCA.keyprivate.
If both mode flags are enabled, Kubernetes mode takes precedence and no Firefox root CA is generated.
Verify before deployment
These verification examples use OpenSSL 3.x. On macOS, the bundled openssl may be LibreSSL and may not support these options; use an OpenSSL 3.x binary for the commands below.
Inspect the certificate’s dates and Subject Alternative Names (SANs):
openssl x509 -in ssl/lab.com.pem.crt \
-noout -subject -issuer -dates -ext subjectAltName
For the default self-signed certificate, verify a hostname against that certificate:
openssl verify -CAfile ssl/lab.com.pem.crt \
-verify_hostname lab.com ssl/lab.com.pem.crt
For Firefox mode, verify the server certificate against its root CA:
openssl verify -CAfile ssl-firefox/lab.com.rootCA.pem \
-verify_hostname lab.com ssl-firefox/lab.com.pem.crt
A successful result ends with OK. Use -verify_ip 127.0.0.1 instead of -verify_hostname lab.com to check the IP address.
Passing these checks confirms the certificate and configured trust relationship. You still need to configure trust in the actual client and ensure the server serves the intended certificate.
Configuration
Every option supports an environment variable and a matching --NAME=value flag. On current source, precedence is:
Non-empty explicit CLI value → environment variable → built-in default.
An omitted or empty CLI value falls back to the environment/default. Pass each flag separately and quote values containing spaces or wildcard characters:
CERT_DNS="lab.com,127.0.0.1" ./certs-maker \
--EXPIRE_DAYS=30 "--CERT_CN=Lab Server" --CUSTOM_FILE_NAME=lab-server
Common options
| Option | Default | Purpose |
| --- | --- | --- |
| CERT_DNS | lab.com,.lab.com,.data.lab.com | Comma-separated DNS names, wildcard domains, and IP addresses. |
| EXPIRE_DAYS | 3650 | Certificate validity in days. Current source validates a positive integer. |
| FOR_K8S | off | Enable Kubernetes certificate extensions and naming. |
| FOR_FIREFOX | off | Generate a local root CA and signed server certificate. |
| DIR | ./ssl | Output directory in the process’s filesystem. |
| CUSTOM_FILE_NAME | empty | Replace the output file prefix, without an extension. |
Use ON and OFF for mode flags. on, true, and 1 are true, ignoring case; other values are false.
CERT_DNS must contain at least one valid address. Invalid entries are discarded, duplicates are removed, and DNS names are lowercased. Bare localhost is not accepted by the normal input parser; Kubernetes mode adds it separately. CERT_CN changes the subject common name, not the SAN list.
Subject fields are configurable through CERT_C, CERT_ST, CERT_L, CERT_O, CERT_OU, and CERT_CN. The complete configuration reference lists all defaults, expiry validation limits, input rules, and ownership options.
Output and permissions
The default prefix is derived from the first valid address. Kubernetes mode adds .k8s; CUSTOM_FILE_NAME replaces the complete prefix. Use simple file names and relative directories such as ssl or certs/server: custom DIR values are normalized, so absolute paths, spaces, and case-sensitive names may change.
With Docker, DIR refers to a container path. Change the bind-mount source to choose another host location.
The container ownership helper requires USER, UID, and GID together. It uses Alpine account tools and makes output files readable by all users, including private keys. Review ownership and key permissions before enabling it. Manage ownership with your operating system’s tools on native installations.
Replacing certificates
Each run generates new keys and overwrites files with the same output prefix. Save existing files elsewhere or use another directory/prefix before creating a replacement.
Firefox mode also regenerates the root CA. After replacement, deploy the new server certificate/key pair and update clients to trust the new public root certificate. An older root CA cannot verify certificates signed by the new one.
Troubleshooting
- Hostname or IP mismatch: inspect SANs and include the address clients actually use in
CERT_DNS. A common name alone does not establish hostname coverage. - Unknown issuer / untrusted certificate: configure the trust store used by that client. For Firefox mode, import the root CA from the same generation as the deployed server certificate.
- Custom validity ignored or Firefox verification fails on v3.8.1: use current source or a newer release containing the fixes.
- Missing output: check the bind mount, output directory, and command exit status. Generation errors can leave partial files; confirm the run succeeded before deploying them.
More information
- Installation and release compatibility
- Certificate modes, verification, and trust
- Complete configuration reference
- Docker Hub
- Creating a lightweight Docker tool image for self-signed certificates
- How to create and use self-signed certificates
License
MIT.