Molixa Forge

Blog / Tutorial

How to Set Up Free SSL on Your VPS (Step by Step)

Published · 10 min read

HTTPS is the default web in 2026. Chrome flags plain HTTP as insecure, search engines quietly rank HTTPS higher, and every modern auth library refuses cookies on unencrypted origins. The good news: SSL is free, well documented, and most VPS workflows can ship a trusted cert in under five minutes. This post walks through it with real commands and the small gotchas that catch people out. By the end you will have a working cert, a renewal timer, and a clear mental model of where things can go wrong.

What Let’s Encrypt is, in one paragraph

Let’s Encrypt is a free, automated certificate authority run by the Internet Security Research Group. Its certs are trusted by every major browser and mobile OS. Each cert lives for 90 days and is designed to be renewed automatically by a small agent running on your server. You never pay, you never fill out a form, and the TLS handshake looks identical to any paid DigiCert or Sectigo cert.

Short lifetimes sound annoying but they are the feature. Short certs limit the damage when a private key leaks, and they force you to run a renewal cron, which means renewal almost never breaks at 3am the way it used to with five-year certs.

Under the hood, Let’s Encrypt speaks ACME, a standardized protocol any client can implement. Certbot is the reference client, but there are good alternatives: acme.sh is a pure shell script, lego is a single Go binary, and Caddy web server issues certs on its own without a separate tool. The steps below use certbot because it is the default on most distros and the one search engines return when someone types “free ssl vps” into their browser.

What you need before you start

Three things. If any of these are missing, the issue flow will fail with a confusing error.

  • A VPS with a reachable public IPv4 address. Any provider works: Hetzner, DigitalOcean, Linode, AWS Lightsail, Contabo.
  • A domain you control, with access to the DNS zone so you can add an A record.
  • Port 80 open on the server and on the provider firewall. The HTTP-01 challenge reaches your server over port 80, even if your final site runs on 443 only.

That last point trips up a lot of people. UFW default deny, Hetzner cloud firewall, AWS security groups, any of these can silently block port 80 and you will stare at a generic connection error. Check the provider firewall first, then the host firewall.

Step 1: point your DNS

In your registrar or DNS host (Cloudflare, Route 53, Namecheap, Hetzner DNS, wherever), add an A record from the name you want (root or subdomain) to your server’s public IPv4 address.

On Cloudflare, leave the proxy off (the grey cloud) until the cert is issued. Cloudflare terminates TLS at their edge and hands a Full or Flexible mode to your origin, which can confuse the HTTP-01 check during issuance.

Propagation on modern managed DNS is fast. Under 15 minutes is typical, often under 60 seconds. If your registrar still quotes 24 to 48 hours, that number is from a different decade and you can ignore it for initial issuance.

Step 2: verify DNS before you issue

The single most common reason Let’s Encrypt refuses to issue a cert is a DNS record that does not point where you think it does. Maybe you forgot to save, maybe you edited the wrong zone, maybe a cached CNAME is overriding you. Check before you issue.

dig +short A yourdomain.com

The output should be a single line: your server’s IPv4. If it is blank, propagation has not completed. If it is a different IP, you edited the wrong record. If it returns two IPs, you have an old record hanging around that needs deleting.

Inside Molixa Forge, the SSL tab on every site includes a one-click Check DNS button that runs this lookup against multiple public resolvers and flags mismatches before you hit Issue.

Step 3: install certbot

Certbot is the official Let’s Encrypt client, maintained by the EFF. On Debian or Ubuntu, install the package along with the nginx plugin so certbot can auto-edit your vhost.

sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx

On RHEL or Rocky, the package name is the same and lives in EPEL. On Alpine, it is certbot certbot-nginx in the community repo. One apt-get line and you are done.

In Molixa Forge, certbot is installed as part of the server bootstrap the first time you connect. No manual step, no sudo prompt, no package hunt.

Step 4: issue the certificate

Two methods, pick the one that fits your setup.

Method A: the nginx plugin

If you already have an nginx server block listening on port 80 for your domain, the nginx plugin is the simplest path. Certbot edits the vhost for you, adds the TLS listener on 443, and reloads nginx.

sudo certbot --nginx -d yourdomain.com

You will be asked for an email (for expiry notifications) and whether to redirect HTTP to HTTPS. Say yes to the redirect unless you have a specific reason not to.

Method B: webroot

If you run your own nginx config (shared config, custom template, Kubernetes ingress), use webroot mode. You tell certbot where your web root is; it drops the challenge file there and lets your existing server serve it.

sudo certbot certonly --webroot -w /var/www/yourdomain -d yourdomain.com

You then wire the issued cert (in /etc/letsencrypt/live/yourdomain.com/) into your nginx config manually. More control, more steps.

What is the HTTP-01 challenge, quickly

To prove you control the domain, Let’s Encrypt asks you to place a secret file at a known URL over plain HTTP on port 80. Their server fetches http://yourdomain.com/.well-known/acme-challenge/XYZ. If the file matches, they sign the cert. That is why DNS has to be right and port 80 has to be open.

Step 5: auto-renew via systemd

The Debian and Ubuntu certbot package installs a systemd timer called certbot.timer that runs twice a day. It checks every cert on the box and renews any that expire in under 30 days. You do not need to write a cron entry.

Verify it is actually there and active:

systemctl list-timers | grep certbot

You should see a line showing the next scheduled run. If you see nothing, enable it with sudo systemctl enable --now certbot.timer. If you want to dry-run the renewal without actually touching files, run sudo certbot renew --dry-run. That is the command to put in a one-time CI job after any nginx config change.

Common errors and what they actually mean

DNS problem: NXDOMAIN looking up A

The domain has no A record at all. Either you never added one, or you typed the subdomain wrong, or DNS has not propagated yet. Re-run the dig command from Step 2 and confirm an IP comes back.

DNS problem: server-ip mismatch

DNS resolves, but not to this server. Classic cause: you migrated servers and forgot to update the A record. Also common when Cloudflare proxy is on and the origin cannot be reached on plain HTTP.

Rate limit: too many duplicate certs

Let’s Encrypt limits production issuance to 5 duplicate certs per week per exact set of names. If you are iterating on config, run certbot against the staging endpoint first with --staging. Staging has much higher limits and produces a fake cert you can test with.

Connection refused on port 80

The challenge server could not reach you. Provider firewall, host UFW rule, or nginx not running. Run curl -v http://yourdomain.com/ from an outside network (not from the server itself) and see where it fails.

How Molixa Forge does SSL without the terminal

Every step above is real and worth understanding once. But after the third server of the week, most of us do not want to SSH in just to run certbot. The security features in Molixa Forge wrap the whole flow into a dashboard card.

  • DNS preflight. Before you click Issue, we query 8 public resolvers (Google, Cloudflare, Quad9, plus regional) and show you where the A record points on each. Mismatches get a red flag and a one-line fix hint.
  • One-click issue. We run certbot with the nginx plugin, the right vhost, the right webroot. If it fails, we show the real certbot log, not a sanitized spinner.
  • Auto-renewal status. A card on the site page shows last renewal, next renewal, issuer, and expiry. If a renewal ever fails, you get an email 7 days before expiry, not 7 hours.
  • Wildcard via DNS-01. Supported for Cloudflare, Route 53, DigitalOcean, and Hetzner DNS. Add the API token once, issue *.yourdomain.com from the dashboard.

It is the same certbot, the same Let’s Encrypt certs, with a UI that catches the 80% of issues that come from DNS and firewall, not from TLS itself. Pair it with the WordPress workflow or any stack on the full feature list, and SSL is the part you stop thinking about.

Wrapping up

HTTPS on a VPS used to cost real money and require a purchase order. Today it is one apt-get, one certbot, one systemd timer, and you are done. The four things that will actually break your issue are: wrong A record, Cloudflare proxy on, port 80 blocked, and rate limits from reissuing too many times in a row. If you keep those in your head, you can ship a cert on any distro in under ten minutes.

And if you run more than two or three servers, a dashboard that runs the preflight for you pays for itself the first time it catches a typo before you burn a rate limit window. See pricing for the free tier, which includes one-click SSL on every site.

Skip the terminal. Issue SSL in one click.

Free SSL on VPS with Let’s Encrypt | Molixa Forge · Molixa Forge