How to Use nginx-acme on Debian/Ubuntu to Auto-Issue Let's Encrypt SSL for NGINX
Learn how to set up nginx-acme on Debian and Ubuntu, configure NGINX virtual hosts, and get automatic Let's Encrypt SSL certificates with renewals and redirects.
Learn how to automatically issue and deploy TLS certificates on Debian Stable and Ubuntu LTS using nginx-acme, NGINX's official ACME module.
1. What is nginx-acme?
nginx-acme is an official NGINX module that implements the ACME protocol, enabling NGINX to request and renew TLS certificates without requiring a separate ACME client.
If you have used Caddy before, this concept will feel familiar: certificate automation becomes part of your web server configuration.
The module is implemented in Rust (unlike NGINX itself, which is written in C) and supports the following standards:
Based on my testing, it is already suitable for production use.
2. Install N.WTF (NGINX with nginx-acme)
In this tutorial we use N.WTF, packaged by m.ac. It provides an nginx-extras package that includes the nginx-acme module, so you can get started immediately.
First, install the required dependencies:
sudo apt update
sudo apt upgrade -y
sudo apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates
Then, import the N.WTF signing key and add the APT repository:
curl -sSL https://n.wtf/public.key | sudo bash -c 'gpg --dearmor > /usr/share/keyrings/n.wtf.gpg'
sudo bash -c 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirror-cdn.xtom.com/sb/nginx/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/n.wtf.list'
On Debian, you can also enable it via extrepo:
sudo apt update
sudo apt install extrepo -y
sudo extrepo enable n.wtf
Install NGINX:
sudo apt update
sudo apt install nginx-extras -y
3. Prepare the Certificate Directory
Create a directory to store the ACME state and certificates. This example uses /var/cache/nginx/letsencrypt:
sudo mkdir -p /var/cache/nginx/letsencrypt
sudo chown 33:33 /var/cache/nginx -R
Ensure that your domain's A/AAAA records point to this server before proceeding.
4. Configure an NGINX Site (example.com + www Redirect)
This example assumes:
- Your domains are
example.comandwww.example.com - You want
www.example.comto redirect toexample.com - Your contact email is
user@example.com
Edit /etc/nginx/sites-enabled/default with the following configuration:
resolver 8.8.8.8:53 ipv6=off valid=5s;
acme_issuer letsencrypt {
uri https://acme-v02.api.letsencrypt.org/directory;
contact user@example.com;
state_path /var/cache/nginx/letsencrypt;
accept_terms_of_service;
ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
# Keep this enabled unless you have a specific reason to disable it.
# ssl_verify on;
ssl_verify off;
}
acme_shared_zone zone=ngx_acme_shared:1M;
server {
# Listen on port 80 for ACME HTTP-01 validation and HTTP->HTTPS redirects
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# Let nginx-acme handle ACME HTTP-01 challenges.
# Do not block /.well-known/acme-challenge/ here.
location /.well-known/acme-challenge/ {
# nginx-acme serves this internally.
# Keeping the block empty avoids accidental 404s.
}
location / {
return 301 https://$host$request_uri;
}
}
server {
# Standard TLS listening
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
# HTTP/2
http2 on;
# HTTP/3 (QUIC)
listen 443 quic reuseport;
listen [::]:443 quic reuseport;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
add_header X-Protocol $server_protocol always;
server_name example.com;
root /var/www/html;
index index.html;
# Modern TLS settings
ssl_protocols TLSv1.3;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_prefer_server_ciphers off;
# Enable certificate automation for this server block
acme_certificate letsencrypt;
ssl_certificate $acme_certificate;
ssl_certificate_key $acme_certificate_key;
# Avoid parsing the certificate on every request
ssl_certificate_cache max=2;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
listen 443 quic;
listen [::]:443 quic;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
add_header X-Protocol $server_protocol always;
server_name www.example.com;
return 301 https://example.com$request_uri;
ssl_protocols TLSv1.3;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_prefer_server_ciphers off;
acme_certificate letsencrypt;
ssl_certificate $acme_certificate;
ssl_certificate_key $acme_certificate_key;
ssl_certificate_cache max=2;
}
Validate the configuration and reload NGINX:
sudo nginx -t
sudo nginx -s reload
5. Verify Certificate Issuance
Once NGINX reloads, nginx-acme will automatically request a certificate. You can monitor the access log to observe the process:
sudo tail -f /var/log/nginx/access.log
You should see requests to /.well-known/acme-challenge/ from Let's Encrypt validation servers. Within a few seconds, https://example.com/ should be live with a valid certificate.
Example log entries:
23.178.112.210 - - [15/Jan/2026:16:08:18 +0000] "GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
34.212.137.78 - - [15/Jan/2026:16:08:18 +0000] "GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
6. IP Address Certificates (Short-Lived Profile)
To request a certificate for an IP address, add this line inside the acme_issuer letsencrypt {} block:
profile shortlived;
Additionally, set server_name to the full IP address (you cannot use server_name _ for an IP certificate).
At the time of writing, nginx-acme 0.3.1 may still fail when requesting IP certificates. The development branch already contains a fix, so you may need to wait for the next release for this feature to work reliably.
