Skip to main content

Custom Images and Application Startup

Choose a runtime mode​

SituationRecommended approach
Application runs on the hostRun certone run on the host or install a system service
Application and CertOne share an image, and certificates are required before startupUse certone exec
Application already has a process managerRun CertOne through that manager and explicitly configure local reload operations
Synology DSMSee 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:

  1. Obtain and prepare certificate files. Do not run application validation or reload commands before the application starts.
  2. If online synchronization fails, try the complete, still-valid local certificate cache.
  3. If no usable certificates are available, return a startup failure without starting the application. The container restart policy can retry the workflow.
  4. On explicit authentication rejection, remain blocked without starting the application or continuously reconnecting. Correct credentials and restart the container.
  5. Once certificates are ready, run the original entrypoint while continuing to maintain certificates.
  6. 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:

ApplicationCommon original startup commandValidation commandReload command
nginx/docker-entrypoint.sh nginx -g "daemon off;"/usr/sbin/nginx -t/usr/sbin/nginx -s reload
httpdhttpd-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 .certone directory writable so state and certificates can be updated. config.yaml only 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 explicit STOPSIGNAL SIGTERM.
  • For non-root images, confirm that the container user can read credentials, write directories, manage the application, and read generated certificates.
  • Running sync --once inside a running container competes with the persistent agent for the state directory. Use status or logs while it runs; stop the persistent process before manual synchronization.