Custom Images and Application Startup
Choose a runtime mode
| Situation | Recommended approach |
|---|---|
| Application runs on the host | Run certone run on the host or install a system service |
| Application and CertOne share an image, and certificates are required before startup | Use certone exec |
| Application already has a process manager | Run CertOne through that manager and explicitly configure local reload operations |
| Synology DSM | See Synology NAS Deployment |
A separate container that only writes to a shared volume does not automatically gain the ability to reload applications in other containers. For nginx and httpd, placing the binary in the application image is usually more direct.
For complete Dockerfile examples using environment variables, see nginx, httpd, and OpenResty or Caddy, HAProxy, and Mosquitto. The configuration-file example below is for advanced deployment rules.
Build an nginx example image
This example runs as root in the container, using /root/.certone. For a non-root image, use that user's home directory and make the mounted directory writable. Configuration paths must be absolute. You can explicitly set output paths to retain the application's existing certificate locations.
Prepare this working directory:
my-nginx/
├── Dockerfile
├── certone-linux # Linux binary matching the target image architecture
├── certone/
│ └── config.yaml
└── nginx/
└── site.conf
Dockerfile:
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
COPY --chmod=0755 certone-linux /usr/local/bin/certone
STOPSIGNAL SIGTERM
ENTRYPOINT ["/usr/local/bin/certone", "exec", "--config", "/root/.certone/config.yaml", "--", "/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
This example preserves /docker-entrypoint.sh in an nginx image. For other images, check the original entrypoint first. Retain initialization scripts that generate configuration or set permissions.
certone/config.yaml:
node:
id: node-REPLACE
token: REPLACE_WITH_NODE_TOKEN
state_dir: /root/.certone/state
sync_interval: 1h
logging:
level: info
format: text
targets:
- name: nginx
type: files
certificates:
- key_path: /root/.certone/certificates/{id}/key.pem
fullchain_path: /root/.certone/certificates/{id}/fullchain.pem
check:
argv: [/usr/sbin/nginx, -t]
timeout: 15s
reload:
argv: [/usr/sbin/nginx, -s, reload]
timeout: 15s
verify:
address: 127.0.0.1:443
server_name: example.com
Replace the node ID and website domain. Leave {id} in output paths for automatic expansion, and put the node Token in node.token in certone/config.yaml. This example uses standard container logs and does not set logging.file.
Minimal nginx/site.conf:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /root/.certone/certificates/cert-REPLACE/fullchain.pem;
ssl_certificate_key /root/.certone/certificates/cert-REPLACE/key.pem;
location / {
return 200 "HTTPS is ready\n";
}
}
Choose the nginx version or image digest when building:
docker build --build-arg BASE_IMAGE=nginx:YOUR_TAG -t my-nginx-certone:local .
Start:
docker run -d --name my-nginx --init --restart unless-stopped \
--log-opt max-size=10m --log-opt max-file=5 \
-p 443:443 \
-v "$PWD/certone:/root/.certone" \
-v "$PWD/nginx/site.conf:/etc/nginx/conf.d/default.conf:ro" \
my-nginx-certone:local
Inspect the results:
docker logs --tail 100 my-nginx
docker exec my-nginx certone status --config /root/.certone/config.yaml
What exec mode does
certone exec --config /root/.certone/config.yaml --timeout 2m -- original-entrypoint arguments
Arguments before -- belong to CertOne; arguments after it are the application's original startup command. The default --timeout is 2m, the maximum wait for the initial online synchronization phase. It does not guarantee continuous retries for the full two minutes.
Startup sequence:
- Obtain and prepare certificate files. Do not run application validation or reload commands before the application starts.
- If online synchronization fails, try the complete, still-valid local certificate cache.
- If no usable certificates are available, return a startup failure without starting the application. The container restart policy can retry the workflow.
- On explicit authentication rejection, remain blocked without starting the application or continuously reconnecting. Correct credentials and restart the container.
- Once certificates are ready, run the original entrypoint while continuing to maintain certificates.
- Exit when the application exits, and forward stop signals to the application when the container stops.
exec supports file targets only. Immediately after startup, an application may not yet accept reloads or listen on its port. Check subsequent retries rather than judging the result from the first startup log entry.
httpd, OpenResty, and other images
Common entrypoints and commands are listed below. Verify paths in your actual image:
| Application | Common original startup command | Validation command | Reload command |
|---|---|---|---|
| nginx | /docker-entrypoint.sh nginx -g "daemon off;" | /usr/sbin/nginx -t | /usr/sbin/nginx -s reload |
| httpd | httpd-foreground | /usr/local/apache2/bin/httpd -t | /usr/local/apache2/bin/httpd -k graceful |
| OpenResty | /usr/local/openresty/bin/openresty -g "daemon off;"; preserve any entrypoint script first | /usr/local/openresty/bin/openresty -t | /usr/local/openresty/bin/openresty -s reload |
For example, an httpd wrapper entrypoint can use:
ENTRYPOINT ["/usr/local/bin/certone", "exec", "--config", "/root/.certone/config.yaml", "--"]
CMD ["httpd-foreground"]
You must still enable HTTPS in the application and reference the certificate paths produced by CertOne.
Docker notes
- Mount the certificate directory, not individual output certificate files. Replacement requires creating temporary files and renaming them in the parent directory.
- Keep a mounted
.certonedirectory writable so state and certificates can be updated.config.yamlonly needs read access. If configuration is mounted separately as read-only, state and output directories must remain writable. - Use a separate state volume for each replica. Do not copy a state directory with an existing instance identity into the image.
- Restart the container after editing configuration. Rebuild the image and recreate the container after updating the binary.
- Retain the state volume to preserve identity, cache, and recovery information. Deleting it is not a routine troubleshooting step.
- Use
--init, an appropriate restart policy, and an explicitSTOPSIGNAL SIGTERM. - For non-root images, confirm that the container user can read credentials, write directories, manage the application, and read generated certificates.
- Running
sync --onceinside a running container competes with the persistent agent for the state directory. Usestatusor logs while it runs; stop the persistent process before manual synchronization.