Debian

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.

3 min read
How to Use nginx-acme on Debian/Ubuntu to Auto-Issue Let's Encrypt SSL for NGINX

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.com and www.example.com
  • You want www.example.com to redirect to example.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.

On this page
Share this: