iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Antihost offers VPS plans with one-click Docker, NVMe storage and a 14-day free trial.
Put NGINX at the public edge, keep your Node.js container on a private Docker Compose network, and terminate TLS at NGINX. Let’s Encrypt then supplies the certificate, while persistent certificate volumes and an automated renewal job keep HTTPS working after container recreation.
The reliable sequence is: publish only NGINX on ports 80 and 443; make port 80 serve the ACME challenge and redirect ordinary requests to HTTPS; configure the port-443 server with the certificate and private-key paths; proxy requests to the internal Node.js service; then test and schedule renewal before switching from staging to production.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallWhat the finished architecture looks like
The public hostname resolves to the Docker host. NGINX is the only service bound to the host’s internet-facing ports. It accepts HTTP and HTTPS connections, performs TLS termination, and forwards application traffic across a private Compose network to Node.js. The Node.js process can therefore listen on ordinary HTTP internally without holding a private key or certificate.
Internet
|
DNS A/AAAA records
|
Docker host: ports 80 and 443
|
NGINX (public reverse proxy and TLS endpoint)
|
private Compose network
|
Node.js app (internal HTTP upstream)
This separation gives each component one job. NGINX handles certificates, redirects, and encrypted connections. Node.js handles application requests. A certificate renewal process writes new files to persistent storage and triggers an NGINX reload or container restart so the proxy begins using them.
#1 Best Overall
Prerequisites and decisions to make first
Public DNS and hostname
Choose the exact hostname that will appear in the certificate. Its A record must point to the public IPv4 address of the Docker host. If the hostname has an AAAA record, that IPv6 address must reach the same NGINX service; an incorrect AAAA record can make validation fail even when IPv4 works.
Firewall and port exposure
Allow inbound TCP 80 and 443 at the cloud firewall, host firewall, and any upstream router. Publish those ports from NGINX only. Do not publish the Node.js container port to the host unless you have a separate operational reason; NGINX should reach it by Compose service name on the private network.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Choose the ACME challenge
HTTP-01 is the straightforward choice when the certificate hostname can reach NGINX on port 80. DNS-01 is appropriate when inbound HTTP cannot be exposed or when a wildcard certificate is required, but it needs credentials for your DNS provider’s API and correct public DNS behavior.
Choose where certificate state lives
You can install Certbot on the Docker host, or run an ACME companion container alongside NGINX. Host-installed Certbot is familiar when the host already manages renewals. A companion keeps issuance and renewal inside Compose, but requires deliberate volume mounts and a reload or restart hook.
Compose layout: keep Node.js private
Create one network shared by the proxy and application. Publish ports only on the NGINX service. Mount certificate files and ACME state from persistent volumes or host directories so a docker compose down, image update, or container recreation does not erase them.
Rank #2
services:
app:
build: ./app
networks:
- internal
nginx:
build: ./nginx
depends_on:
- app
ports:
- "80:80"
- "443:443"
networks:
- internal
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- certs:/etc/letsencrypt:ro
- acme-webroot:/var/www/certbot:ro
certbot:
image: certbot/certbot
volumes:
- certs:/etc/letsencrypt
- acme-webroot:/var/www/certbot
networks:
internal:
driver: bridge
volumes:
certs:
acme-webroot:
The exact application and NGINX image tags depend on your build. The security properties are the important part: app has no published port, NGINX publishes 80 and 443, and both NGINX and the certificate client share durable certificate and ACME-challenge storage. If you use a companion instead of a separate Certbot service, give it the same persistent certificate and ACME-state mounts required by that companion’s Compose configuration.
Configure NGINX for validation, redirect, and proxying
Port 80: leave the challenge reachable
Certbot’s webroot flow and HTTP-01 validation need an HTTP site that is reachable on port 80. Serve the ACME directory directly, then redirect all other requests to HTTPS. The challenge location must be evaluated before the general redirect.
server {
listen 80;
listen [::]:80;
server_name example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
Replace example.com with the certificate hostname. The shared /var/www/certbot path must be writable by the ACME client and readable by NGINX. Test this path before requesting a production certificate; an application-level redirect or catch-all route must not intercept it.
Port 443: terminate TLS and proxy to Node.js
The HTTPS virtual server needs the certificate and private-key paths. Its upstream target is the Compose service name and the port on which Node.js listens inside the network.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://app:3000;
proxy_http_version 1.1;
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_set_header X-Forwarded-Proto $scheme;
}
}
The service name app is resolved through the Compose network; it is not a public DNS name. Change port 3000 to the internal listening port used by your Node.js process. Keep the private key readable only by the NGINX process and the renewal workflow that must install it.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Rank #3
Validate before reloading
After each configuration change, run nginx -t inside the NGINX container. A successful syntax check is necessary but does not prove that DNS, ports, certificate files, or the upstream application are reachable. Reload only after the test passes.
Obtain the first certificate with Certbot
Using the NGINX installer on the host
Certbot’s NGINX instructions provide sudo certbot --nginx. This mode obtains a certificate and edits NGINX configuration automatically. It normally expects an HTTP site already online and reachable on port 80. If NGINX runs inside Compose, host-installed Certbot must still be able to read and write the same certificate directories and must be able to reload the container after renewal.
- Point the hostname’s A and, if used, AAAA records to the Docker host.
- Start NGINX with the port-80 server and the ACME challenge location, but do not request a production certificate until the challenge path is reachable.
- Run
sudo certbot --nginx, select the hostname, and allow the client to install the HTTPS configuration when prompted. - Review the generated server blocks. Confirm that the challenge location remains reachable and that application traffic is redirected to HTTPS.
- Run
nginx -t, reload NGINX, and request the site over HTTPS.
Certbot also documents certbot --webroot. Webroot is often easier to reason about in Compose because the client writes a token into the shared ACME directory while NGINX serves that directory. The --apache mode is for an Apache deployment, not an NGINX proxy.
Using a dedicated ACME companion
An ACME companion container can watch the configured hostname, obtain certificates, store ACME state, and coordinate with NGINX. Its Compose arrangement requires persistent certificate and state volumes. Configure the ACME hostname to a name that resolves publicly to the Docker host, and ensure the companion and NGINX share the paths expected by that implementation.
Free tools Windows power users keep installed
One-click scans. No signup required.
This approach avoids installing the client on the host, but the operational ownership moves into Compose: upgrades, logs, credentials, volume backups, and the post-renewal reload command all belong in the containerized stack. Follow the companion’s documented environment variables for HTTP-01 or DNS-01 rather than inventing names or paths.
Rank #4
HTTP-01 and DNS-01: choose deliberately
| Decision point | HTTP-01 | DNS-01 |
|---|---|---|
| Inbound requirement | Port 80 must reach NGINX for validation. | No inbound HTTP validation is required, but public DNS must update correctly. |
| Wildcard certificates | Not suitable for wildcard issuance. | Supports wildcard certificates. |
| Configuration | Serve the token from the shared webroot and keep the challenge path out of application redirects. | Configure the DNS provider’s API integration and the required environment variables. |
| Credential exposure | No DNS API credential is needed for the challenge. | Store a narrowly scoped DNS API credential where the ACME client can use it without exposing it to the Node.js app. |
| Typical failure | Port 80 blocked, wrong A/AAAA record, or a redirect/application route intercepts the token. | DNS API permissions, propagation, public resolution, or IPv6 reachability is incorrect. |
Use HTTP-01 when a normal public web service is possible. Use DNS-01 when policy or network design prevents inbound port 80, or when the certificate must cover a wildcard name. In either case, validate DNS from outside the host and check both IPv4 and IPv6 paths before switching to production issuance.
Persistent storage is part of the security design
Certificates are files, not container metadata. Mount the certificate directory into both the process that renews certificates and the NGINX process that serves them. Persist ACME account and renewal state as well. Without those mounts, recreating a container can remove the files and force an unnecessary reissuance attempt.
- Back up the certificate and ACME-state volumes using your normal server backup process.
- Do not commit private keys, DNS API tokens, or certificate directories to source control.
- Use read-only mounts for NGINX where the container only needs to serve existing files.
- Ensure the renewal client can write new certificates and that NGINX can read the resulting paths.
- Document which command reloads NGINX after a successful renewal.
Automate renewal without breaking traffic
Renewal automation has two separate responsibilities: run the ACME client on a schedule, and make the running proxy load the renewed files. A successful certificate update without a reload leaves existing NGINX workers using the old certificate until they are restarted or reloaded.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Host-installed workflow
Schedule the host’s Certbot renewal command using the operating system’s scheduler or service manager. Configure a deploy hook that reloads NGINX only when Certbot reports a successful renewal. If NGINX runs in Compose, the hook must target the container, for example by invoking the appropriate docker compose reload or restart operation from the host.
Containerized workflow
Run the companion or Certbot container on a schedule and mount the same persistent volumes into NGINX. Use the companion’s documented reload mechanism, or run a controlled NGINX reload after renewal. Do not rely on a one-time issuance command; the process must run repeatedly and retain its ACME account state.
Best Value
Test before production
Use the client’s dry-run or staging mode while iterating. The companion pattern supports a test-certificate setting and a configurable ACME CA URI. Staging certificates are intended to verify DNS, challenge routing, volume mounts, and reload hooks without consuming production issuance capacity. Once a complete dry run succeeds, switch to the production CA endpoint and request the real certificate.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Preflight checks before requesting a production certificate
- Resolve the hostname from an external network and verify the A record points to the deployment host.
- If an AAAA record exists, verify that IPv6 reaches the same NGINX listener and firewall policy.
- Connect to port 80 from outside the host and confirm NGINX, not a different service, answers.
- Place a harmless test file under
/.well-known/acme-challenge/and confirm it is served through the public hostname. - Confirm the challenge request is not redirected into the Node.js application or blocked by an authentication layer.
- Connect to port 443 after installing a test certificate and verify that NGINX can read both certificate files.
- From NGINX, resolve the Compose service name and confirm the Node.js process is listening on the configured internal port.
- Recreate the stack and verify that certificate and ACME-state files remain present.
- Run the renewal dry run and observe the expected NGINX reload or restart.
Troubleshooting by symptom
“Connection refused” on the challenge
Check the host and cloud firewalls, port publishing, and whether another service occupies port 80. Confirm that DNS points to the correct public address. If IPv6 is published, test it separately; an unreachable AAAA address can cause validators to fail before IPv4 is tried.
The challenge returns the Node.js app or a 301 loop
Inspect the port-80 server block and place the /.well-known/acme-challenge/ location before the general redirect. Confirm that the webroot volume is mounted at exactly the same path in NGINX and the ACME client. Remove application middleware that rewrites or authenticates the challenge path.
NGINX reports a missing certificate
Verify that the certificate volume is mounted into the NGINX container, that the hostname directory matches the certificate name, and that the files exist after issuance. Check file permissions and run nginx -t inside the container. A container recreated without the persistent volume will not contain the previous certificate.
HTTPS works, but the app returns 502
Check that the Node.js service is attached to the same Compose network, that the upstream name matches the service key, and that the internal port is correct. Test connectivity from inside the NGINX container rather than using the host-published address.
DNS-01 validation fails
Confirm that the DNS API credential has permission to create the required TXT record, that the provider configuration is loaded into the ACME client, and that the TXT record is publicly visible. Recheck AAAA routing and wait for authoritative DNS responses before retrying.
Renewal succeeds but the old certificate is still served
Inspect the renewal logs and confirm that the deploy hook ran. Reload or restart NGINX after the files change, then make a new external TLS connection. If the hook targets a host-installed NGINX while the proxy actually runs in Compose, change it to target the containerized service.
Quick Recap
Operational security checklist
- Expose only ports 80 and 443 from the Docker host; keep the Node.js port private.
- Redirect normal HTTP traffic to HTTPS, while exempting only the ACME challenge path.
- Keep private keys and DNS API credentials out of the application container and source repository.
- Persist certificate and ACME state, and protect backups containing private keys.
- Use staging or dry-run validation before production issuance.
- Monitor renewal logs and test that NGINX reloads after a successful renewal.
- Recheck DNS A and AAAA records whenever the host’s public address changes.
- After changing NGINX configuration, run
nginx -tbefore reloading.
End-to-end implementation checklist
- Create the Compose network and define
app,nginx, and either Certbot or an ACME companion. - Remove any host port publishing from the Node.js service.
- Publish 80 and 443 on NGINX and mount persistent certificate, ACME-state, and challenge directories.
- Deploy the port-80 server block with a working challenge location and HTTPS redirect.
- Point public DNS to the host and verify external reachability, including IPv6 when applicable.
- Run a staging or dry-run issuance flow.
- Install the port-443 server block with
ssl_certificate,ssl_certificate_key, andproxy_passto the internal Node.js service. - Run
nginx -t, reload NGINX, and verify an external HTTPS request. - Schedule renewal and connect a successful-renewal hook to an NGINX reload or container restart.
- Recreate the Compose stack and repeat the HTTPS and renewal checks to prove that persistence works.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

