DEV Community

Stanley
Stanley Subscriber

Posted on

Migrating from Nginx to Angie: A Real-World Journey from Certbot to Built-in ACME

Introduction

When it comes to web servers, nginx has been the gold standard for years. However, the emergence of Angie—a modern fork of nginx developed by some of the original nginx team members—offers compelling reasons to consider migration. This article chronicles a real-world migration from nginx with certbot to Angie with its built-in ACME module, including the challenges faced and solutions implemented.

Why Migrate to Angie?

Angie brings several improvements over nginx:

  • Built-in ACME support: No more external certificate management tools
  • Enhanced features: Advanced load balancing, improved monitoring, and better HTTP/3 support
  • Active development: Regular updates and new features from the team that helped create nginx
  • Backward compatibility: Most nginx configurations work with minimal changes

The Migration Challenge

Our production environment was running:

  • Web Server: nginx
  • SSL Management: certbot for Let's Encrypt certificates
  • Application: Next.js application proxied through nginx
  • Domains: Multiple domains (tangaacademie.com, api.tangaacademie.com)

The goal was to migrate to Angie while replacing certbot with Angie's native ACME module for automated certificate management.

Step 1: Understanding Angie's ACME Module

Unlike certbot, which is an external tool that modifies your web server configuration and manages certificates separately, Angie's ACME module is built directly into the web server. This means:

  1. No external processes: Everything happens within Angie
  2. Configuration-based: Certificates are managed through Angie's config files
  3. Variable-based access: Certificates are exposed as embedded variables
  4. Automatic renewal: Handled internally without cron jobs

Key Concepts

The ACME module works through:

  • ACME clients: Defined in the http block with unique names
  • Server references: Individual servers reference the ACME client
  • Certificate variables: $acme_cert_<name> and $acme_cert_key_<name>
  • Automatic validation: Built-in handlers for HTTP, DNS, and ALPN challenges

Step 2: Installing Angie

First, we installed Angie following the official documentation:

# For RHEL/CentOS/AlmaLinux
sudo dnf install angie

# Start and enable Angie
sudo systemctl start angie
sudo systemctl enable angie
Enter fullscreen mode Exit fullscreen mode

Step 3: Initial Configuration Migration

Our original nginx configuration looked like this:

# Original nginx config with certbot
server {
    listen 80;
    server_name tangaacademie.com www.tangaacademie.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name tangaacademie.com www.tangaacademie.com;

    ssl_certificate /etc/letsencrypt/live/tangaacademie.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/tangaacademie.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
Enter fullscreen mode Exit fullscreen mode

We migrated this to Angie with ACME:

# /etc/angie/angie.conf or /etc/angie/sites-enabled/tangaacademie.conf

http {
    # Critical: DNS resolver for ACME
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # Define ACME client
    acme_client tangaacademie https://acme-v02.api.letsencrypt.org/directory;

    server {
        listen 80;
        server_name tangaacademie.com www.tangaacademie.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name tangaacademie.com www.tangaacademie.com;

        # Reference ACME client
        acme tangaacademie;

        # Use ACME-managed certificates via variables
        ssl_certificate $acme_cert_tangaacademie;
        ssl_certificate_key $acme_cert_key_tangaacademie;

        location / {
            proxy_pass http://localhost:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            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;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: The DNS Resolver Challenge

After initial configuration, we encountered our first major issue. The Angie error logs showed:

[error] acme-v02.api.letsencrypt.org could not be resolved (5: Operation refused)
[error] request to ACME server failed: -1, ACME client: tangaacademie
Enter fullscreen mode Exit fullscreen mode

The Problem

We initially configured the resolver as:

resolver 127.0.0.53;
Enter fullscreen mode Exit fullscreen mode

This is a common systemd-resolved stub resolver on modern Linux systems. However, testing revealed the issue:

$ dig @127.0.0.53 acme-v02.api.letsencrypt.org
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
status: REFUSED
Enter fullscreen mode Exit fullscreen mode

The local resolver was refusing recursive queries, which the ACME module requires to contact Let's Encrypt servers.

The Solution

We switched to public DNS resolvers:

# Google DNS
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# Alternative: Cloudflare DNS
# resolver 1.1.1.1 1.0.0.1 valid=300s;
# resolver_timeout 5s;
Enter fullscreen mode Exit fullscreen mode

After this change, the ACME module successfully connected to Let's Encrypt.

Step 5: Verification and Testing

After applying the configuration, we verified the setup:

# Test configuration
angie -t

# Reload Angie
systemctl reload angie

# Watch for ACME activity
tail -f /var/log/angie/error.log
Enter fullscreen mode Exit fullscreen mode

The logs showed successful certificate acquisition:

[notice] creating implicit client block for "@acme"
[notice] ACME: requesting certificate for tangaacademie.com
Enter fullscreen mode Exit fullscreen mode

We verified the certificates were issued:

$ openssl x509 -in /var/lib/angie/acme/tangaacademie/certificate.pem -noout -issuer -subject -dates
issuer=C=US, O=Let's Encrypt, CN=E7
subject=CN=tangaacademie.com
notBefore=Feb  1 11:04:38 2026 GMT
notAfter=May  2 11:04:37 2026 GMT
Enter fullscreen mode Exit fullscreen mode

Perfect! Let's Encrypt had issued valid certificates.

Step 6: Managing Multiple Domains

For our API subdomain, we created a separate ACME client:

http {
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # Main domain ACME client
    acme_client tangaacademie https://acme-v02.api.letsencrypt.org/directory;

    # API subdomain ACME client
    acme_client apitangaacademie https://acme-v02.api.letsencrypt.org/directory;

    # Main domain
    server {
        listen 443 ssl;
        server_name tangaacademie.com www.tangaacademie.com;
        acme tangaacademie;
        ssl_certificate $acme_cert_tangaacademie;
        ssl_certificate_key $acme_cert_key_tangaacademie;

        # ... location blocks
    }

    # API subdomain
    server {
        listen 443 ssl;
        server_name api.tangaacademie.com;
        acme apitangaacademie;
        ssl_certificate $acme_cert_apitangaacademie;
        ssl_certificate_key $acme_cert_key_apitangaacademie;

        # ... location blocks
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Cleaning Up Certbot

After confirming Angie's ACME module was working correctly, we removed certbot:

# Stop certbot renewal timer
sudo systemctl stop certbot.timer
sudo systemctl disable certbot.timer

# Remove certbot
sudo dnf remove certbot  # or apt remove certbot

# Clean up old certificates (optional, after backup)
sudo rm -rf /etc/letsencrypt
Enter fullscreen mode Exit fullscreen mode

Key Differences: Certbot vs. Angie ACME

Aspect Certbot Angie ACME
Integration External tool Built into web server
Configuration Modifies server config Pure config-based
Certificate Access File paths Embedded variables
Renewal Cron job required Automatic internal
Process Separate process Same process as web server
Complexity Moderate Lower (once configured)
Startup Web server independent Certificates ready at startup

Common Pitfalls and Solutions

1. DNS Resolver Issues

Problem: acme-v02.api.letsencrypt.org could not be resolved

Solution: Use public DNS resolvers instead of local stub resolvers:

resolver 8.8.8.8 8.8.4.4 valid=300s;
Enter fullscreen mode Exit fullscreen mode

2. Old Certificate Paths

Problem: cannot load certificate "/etc/angie/"

Solution: Ensure all ssl_certificate directives use ACME variables:

ssl_certificate $acme_cert_<client_name>;
ssl_certificate_key $acme_cert_key_<client_name>;
Enter fullscreen mode Exit fullscreen mode

3. Port 80 Not Open

Problem: HTTP validation fails

Solution: Ensure port 80 is accessible for ACME HTTP challenges. Angie automatically handles /.well-known/acme-challenge/ requests.

4. Missing Resolver Directive

Problem: ACME client can't contact Let's Encrypt

Solution: Always include a resolver directive in the http block.

Advanced Configuration: DNS Validation

For wildcard certificates or when port 80 isn't available, DNS validation is an option:

acme_client example https://acme-v02.api.letsencrypt.org/directory
    challenge=dns;

server {
    listen 443 ssl;
    server_name example.com *.example.com;
    acme example;

    ssl_certificate $acme_cert_example;
    ssl_certificate_key $acme_cert_key_example;
}
Enter fullscreen mode Exit fullscreen mode

This requires configuring your DNS to delegate _acme-challenge. subdomains to your Angie server.

Monitoring and Maintenance

Check Certificate Status

# View certificate details
openssl x509 -in /var/lib/angie/acme/<client_name>/certificate.pem -noout -text

# Check expiration
openssl x509 -in /var/lib/angie/acme/<client_name>/certificate.pem -noout -dates
Enter fullscreen mode Exit fullscreen mode

Monitor ACME Activity

# Watch error log for ACME events
tail -f /var/log/angie/error.log | grep -i acme

# Check certificate directory
ls -la /var/lib/angie/acme/
Enter fullscreen mode Exit fullscreen mode

Debug Mode

For troubleshooting, enable debug logging:

error_log /var/log/angie/error.log debug;
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

Angie's built-in ACME module offers several performance advantages:

  1. Reduced overhead: No external processes or file monitoring
  2. Faster startup: Certificates are loaded at startup, no waiting for certbot
  3. Less disk I/O: Direct memory access to certificates via variables
  4. Simpler architecture: Fewer moving parts means fewer failure points

Security Best Practices

  1. Use strong SSL configuration:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
Enter fullscreen mode Exit fullscreen mode
  1. Enable OCSP stapling:
ssl_stapling on;
ssl_stapling_verify on;
Enter fullscreen mode Exit fullscreen mode
  1. Implement HSTS:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Migrating from nginx with certbot to Angie with built-in ACME proved to be a worthwhile endeavor. The key lessons learned:

  1. DNS resolver configuration is critical - Don't rely on local stub resolvers
  2. ACME variables simplify configuration - No more managing file paths
  3. Built-in integration is elegant - Fewer external dependencies
  4. Test thoroughly - Verify each domain and subdomain independently

The migration resulted in:

  • ✅ Simplified infrastructure (no more certbot)
  • ✅ More reliable certificate renewal
  • ✅ Cleaner configuration
  • ✅ Improved monitoring capabilities
  • ✅ Better performance

For organizations running nginx, Angie represents a compelling upgrade path that maintains compatibility while offering modern features and simplified certificate management.

Resources


About the Author: This migration was performed on a production environment running Next.js applications with multiple domains in February 2026.

Top comments (0)