The Best Price for IPv4/IPv6 Lease – Any RIR & Any Geo-LocationOrder Now
Hostperl

Configure Multi-Domain SSL on Ubuntu VPS with Certbot

By Raman Kumar

Share:

Updated on Jun 24, 2026

Configure Multi-Domain SSL on Ubuntu VPS with Certbot

What You're Actually Solving Here

Running several sites on one VPS is common — a client's main domain, a staging subdomain, a separate landing page. Each one needs HTTPS. Managing a separate certificate per domain gets messy fast, especially when renewals drift out of sync.

This tutorial covers multi-domain SSL on Ubuntu VPS using Certbot and Let's Encrypt. You'll issue a single certificate covering multiple domains and subdomains, configure it for Nginx or Apache, and set up automatic renewal so nothing quietly expires at 2am on a Friday.

Instructions are tested on Ubuntu 22.04 LTS and 24.04 LTS. If you're still choosing a server OS, our Ubuntu vs Debian VPS comparison is worth a quick read first.

Before You Start: Prerequisites

Make sure the following are in place before running any commands:

  • A Hostperl VPS running Ubuntu 22.04 or 24.04 with root or sudo access
  • Nginx or Apache installed and serving at least one domain
  • DNS A records for every domain and subdomain pointing to your VPS public IP — Let's Encrypt verifies each one individually
  • Ports 80 and 443 open in UFW (or your firewall of choice)
  • A valid email address for expiry notifications

DNS propagation is the one thing that trips people up most. Run a quick check before issuing anything:

dig +short yourdomain.com
dig +short www.yourdomain.com
dig +short staging.yourdomain.com

All three should return your server's IP. If any resolve elsewhere, Let's Encrypt's HTTP-01 challenge will fail with a confusing authorization error.

Step 1 — Install Certbot

Ubuntu's apt package for Certbot is often a release behind. Use the Snap version instead — it's what the Let's Encrypt team recommends and updates itself automatically.

sudo apt update
sudo apt install snapd -y
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Confirm it installed correctly:

certbot --version

In 2026, you should see certbot 2.11.x or higher.

Step 2 — Open Firewall Ports

Certbot's HTTP-01 challenge needs port 80 reachable even if you plan to redirect everything to HTTPS afterward. Port 443 handles the final encrypted traffic.

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
sudo ufw status

If you'd rather use named profiles instead of individual port rules, the UFW application profiles guide covers the Nginx and Apache profile setup.

Step 3 — Issue a Multi-Domain Certificate

The -d flag accepts as many domains as you need. Let's Encrypt allows up to 100 SANs (Subject Alternative Names) per certificate, though most VPS setups cover 3–10 domains in practice.

For Nginx

sudo certbot --nginx \
  -d example.com \
  -d www.example.com \
  -d staging.example.com \
  -d anotherdomain.com \
  --email you@example.com \
  --agree-tos \
  --no-eff-email

Certbot detects your existing server blocks, matches them to the domains you specified, and updates the configs automatically. It adds the ssl_certificate and ssl_certificate_key directives and installs a redirect from port 80 to 443.

For Apache

sudo certbot --apache \
  -d example.com \
  -d www.example.com \
  -d staging.example.com \
  -d anotherdomain.com \
  --email you@example.com \
  --agree-tos \
  --no-eff-email

Same principle — Certbot finds your VirtualHost blocks and patches them in place. You'll get a prompt asking whether to redirect HTTP to HTTPS. Choose option 2 unless you have a specific reason not to.

Using the Standalone Plugin (no web server required)

If you're issuing the cert before a web server is configured — or Certbot can't parse your existing config — use the standalone plugin. It spins up a temporary HTTP server on port 80 to complete the challenge:

sudo systemctl stop nginx  # or apache2
sudo certbot certonly --standalone \
  -d example.com \
  -d www.example.com \
  --email you@example.com \
  --agree-tos
sudo systemctl start nginx

The cert files land in /etc/letsencrypt/live/example.com/. You'll need to reference them manually in your server config.

Step 4 — Verify the Certificate

Once Certbot reports success, confirm the certificate covers every domain you listed:

sudo certbot certificates

The output should look something like this:

Found the following certs:
  Certificate Name: example.com
    Domains: example.com www.example.com staging.example.com anotherdomain.com
    Expiry Date: 2026-09-14 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem

Also run a quick live check:

curl -vI https://example.com 2>&1 | grep -E 'SSL|subject|issuer|expire'

You should see Let's Encrypt as the issuer and an expiry date roughly 90 days out.

Step 5 — Configure Your Web Server for the Certificate

If you used the --nginx or --apache plugins, your server blocks were already updated. Still worth reviewing the changes so you know exactly what's there.

Nginx — what a complete SSL server block looks like

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

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/example.com/html;
    index index.html index.php;
}

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

The file /etc/letsencrypt/options-ssl-nginx.conf manages cipher suites and TLS version settings. Certbot keeps it current with sensible defaults — don't override it unless you have a specific compliance requirement.

Apache — the equivalent VirtualHost block

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    DocumentRoot /var/www/example.com/html
</VirtualHost>

After any edits, test before reloading:

# Nginx
sudo nginx -t && sudo systemctl reload nginx

# Apache
sudo apachectl configtest && sudo systemctl reload apache2

Step 6 — Set Up Automatic Renewal

Let's Encrypt certificates expire after 90 days. The Snap version of Certbot installs a systemd timer that checks for renewals twice daily — but confirm it's actually running:

sudo systemctl status snap.certbot.renew.timer

You want to see active (waiting). Then do a dry run to confirm renewal would succeed without replacing anything live:

sudo certbot renew --dry-run

A clean dry run prints Congratulations, all simulated renewals succeeded. If it fails, the output usually identifies exactly which domain failed and why — typically a DNS mismatch or a server block Certbot can't parse.

Post-renewal hook to reload your web server

The Nginx and Apache plugins reload your server automatically after renewal. If you used certonly with the standalone plugin, you'll need a deploy hook to do that yourself:

sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Add this content:

#!/bin/bash
systemctl reload nginx

Make it executable:

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Now every time a cert renews, the hook runs and Nginx picks up the new files immediately.

Adding a New Domain to an Existing Certificate

You issued a cert for four domains last month. A client adds a fifth site. You don't need a new certificate — just expand the existing one:

sudo certbot --nginx \
  -d example.com \
  -d www.example.com \
  -d staging.example.com \
  -d anotherdomain.com \
  -d newdomain.com

Include all current domains plus the new one. Certbot issues a replacement certificate covering the full list. The old cert stays active until the new one is confirmed valid, so there's no downtime.

This is one of the more common questions for multi-site VPS setups — worth bookmarking for when a client adds a new property six months from now. If you manage sites for multiple clients, the guide on VPS hosting for agencies covers how to structure things so domains don't bleed into each other.

Common Errors and What They Mean

Error: DNS problem: NXDOMAIN looking up A for staging.example.com
The subdomain doesn't exist in DNS yet. Create the A record, wait for propagation, then re-run.

Error: Connection refused / Timeout during challenge
Port 80 is blocked. Check UFW with sudo ufw status and confirm your cloud provider's security group isn't overriding it at a higher level.

Error: Certificate is for different domain names
You ran Certbot with a different domain list than the cert currently on disk. Re-run with the complete list, including all existing domains.

Renewal silently failing
Check the logs at /var/log/letsencrypt/letsencrypt.log. Also verify the timer is still scheduled with sudo systemctl list-timers | grep certbot.

For a broader look at keeping your VPS secure alongside SSL management, the VPS security checklist covers the other layers worth having in place.

Running multiple client sites on a single server? Hostperl VPS plans include full root access, Ubuntu 22.04/24.04 support, and enough resources to run Certbot, Nginx, and multiple SSL-secured domains without fighting memory limits. Need more headroom for high-traffic properties? Our dedicated server options give you the room to scale without sharing anything.

Frequently Asked Questions

Can I use one Let's Encrypt certificate for completely different domains?

Yes. The -d flag accepts any combination of domains and subdomains you control, regardless of whether they share a root domain. Each domain must point to the same server and pass the HTTP-01 challenge individually.

What happens if one domain in my certificate fails renewal?

Certbot attempts renewal for the whole certificate at once. If one domain's challenge fails — usually because DNS changed or port 80 is blocked — the entire renewal attempt fails. The existing cert stays active until it actually expires, and Let's Encrypt will send email warnings starting 20 days before that happens.

Does this work with wildcard subdomains like *.example.com?

Yes, but wildcard certs require the DNS-01 challenge instead of HTTP-01. That means you need API access to your DNS provider. Certbot has plugins for Cloudflare, Route 53, and others. The setup is slightly more involved, but it uses the same Certbot tooling.

How do I check which certificate is being served on a live domain?

Run: echo | openssl s_client -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -text | grep -A2 'Subject Alternative Name' — this shows every domain the certificate covers.

Can I mix domains served by Nginx and Apache on the same cert?

The certificate itself is web-server-agnostic. The same fullchain.pem and privkey.pem files work with Nginx, Apache, or any other server — you just reference the correct paths in each server's config.