Back to blog
Tutorial April 21, 2026 9 min read

How to Point a Domain to Your VPS and Get Automatic HTTPS

Two things stand between a fresh VPS and a live site on your own domain: DNS records that resolve to your server's IP, and a TLS certificate that browsers trust. Most "it doesn't work" problems are one of those two, in the wrong order. This guide walks through both — the exact records to create, how to confirm they resolve, and how an ACME client turns a resolving domain into a valid HTTPS certificate that renews itself.

The DNS Records That Actually Matter

You only need a handful of record types to put a web app on a single server.

  • A record maps a name to an IPv4 address. This is what you use for the apex (root) domain, e.g. example.com pointing at 203.0.113.10.
  • AAAA record is the IPv6 equivalent. Only create one if your VPS has a public IPv6 address and your app actually listens on it. A stale AAAA that points nowhere will cause intermittent timeouts for IPv6-capable clients — a nasty, hard-to-spot failure.
  • CNAME record aliases one name to another name (not an IP). It is the right tool for subdomains like www pointing at example.com.

The apex-CNAME problem. The DNS spec forbids a CNAME at the zone apex, because the apex must also carry SOA and NS records and a CNAME cannot coexist with other records on the same name. So you cannot CNAME example.com to another hostname — the apex must be an A (and optionally AAAA) record with a literal IP. Some providers offer "ALIAS" or "ANAME" flattening to fake apex-CNAME behavior, but on a single VPS you do not need it: just put the IP in an A record.

TTL and propagation. TTL is how long resolvers may cache an answer, in seconds. It does not control how fast a change goes out the first time — it controls how long the old answer can linger. If you plan to change records, lower the TTL (to 300, say) a day ahead so caches expire quickly. "Propagation" is just caches worldwide aging out their old copies; a new record on a never-before-queried name resolves almost immediately, while a changed record waits out the previous TTL.

A Worked Example: example.com on 203.0.113.10

Suppose your VPS public IPv4 is 203.0.113.10 and you want the root domain, www, and anapp subdomain all served from it. In your DNS provider's dashboard, create these records:

TypeName / HostValueTTL
A@ (apex)203.0.113.103600
CNAMEwwwexample.com.3600
Aapp203.0.113.103600

The @ symbol means "the zone apex itself" in most control panels. The trailing dot on example.com. in the CNAME makes it fully-qualified so the resolver does not append the zone again. You could equally pointwww at the IP with a second A record — a CNAME to the apex just means you only edit the IP in one place if the server ever moves.

Verify Resolution Before You Touch HTTPS

Do not skip this step. Query the authoritative answer with dig before you even think about certificates.

# Apex should return your VPS IPv4, nothing else
dig +short example.com A
203.0.113.10

# www is a CNAME, so ask for the alias and the resolved A
dig www.example.com CNAME +short
example.com.

dig +short www.example.com A
203.0.113.10

# The app subdomain
dig +short app.example.com A
203.0.113.10

A correct answer is exactly your server's IP and nothing extra. Wrong answers look like: an empty result (record missing or not yet propagated), the IP of a parking page or your registrar's "for sale" server (records never updated), or two different A records where only one is your box (a leftover record you forgot to delete). To bypass your local cache and ask a public resolver directly, append its address:

dig @1.1.1.1 +short example.com A
dig @8.8.8.8 +short example.com A

On a box without dig, usenslookup example.com ornslookup app.example.com 1.1.1.1. Same idea, noisier output — look at the Address: line under the non-authoritative answer.

How Automatic HTTPS Actually Works

Automatic HTTPS means an ACME client (certbot, Caddy, Traefik, etc.) talks to a Certificate Authority — usually Let's Encrypt — and proves you control the domain before the CA signs a certificate. The most common proof is the HTTP-01 challenge:

  • Your client requests a certificate for app.example.com and receives a random token.
  • It places that token at http://app.example.com/.well-known/acme-challenge/<token> on port 80.
  • Let's Encrypt resolves the name via public DNS, fetches that URL, and checks the token matches. If it does, it issues a certificate valid for 90 days.

This is exactly why DNS must resolve first and ports 80 and 443 must be open. The CA reaches your server by the public name over port 80 to validate, then serves traffic over 443. If DNS still points at the registrar parking page, the challenge file is fetched from the wrong machine and validation fails. Open the firewall on both:

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

Renewal is automatic: the client re-runs the challenge well before the 90-day expiry (typically at 60 days) and swaps in the fresh certificate without downtime. There is no annual "buy a new cert"ritual.

Common issuance failures: DNS not yet propagated (the CA still sees the old IP); a firewall or security group silently dropping port 80, so the challenge fetch times out; hitting Let's Encrypt rate limits after many failed retries (use their staging endpoint while you debug); or a CAA record on the domain that names a different CA, which forbids Let's Encrypt from issuing. Check CAA withdig +short example.com CAA — an empty result means any CA is allowed, which is fine.

Troubleshooting Checklist

  • NET::ERR_CERT_AUTHORITY_INVALID: the browser is getting a self-signed or default certificate, not the Let's Encrypt one. Issuance failed or never ran. Re-check that DNS resolves to this box and that port 80 was reachable when the cert was requested.
  • NET::ERR_CERT_COMMON_NAME_INVALID: you have a valid cert, but for the wrong name — e.g. visiting www.example.com when the cert only covers the apex. Make sure every hostname you serve is included in the certificate request.
  • "challenge failed" / urn:ietf:params:acme:error:connection: the CA could not fetch the token. Almost always port 80 blocked, DNS pointing elsewhere, or nothing listening on 80. Test it yourself: curl -I http://app.example.com from another machine.
  • Mixed content warnings: the page loads over HTTPS but pulls scripts, images, or API calls over http://. The certificate is fine; fix the hardcoded URLs to https:// (or protocol-relative) in your app.

Where AODE Fits

Once your A record points at the box and ports 80 and 443 are open, AODE handles the certificate side for you. It runs Traefik as the reverse proxy, and Traefik performs the Let's Encrypt HTTP-01 challenge, stores the certificate, and renews it automatically as expiry approaches. You add a custom domain in the dashboard, point the DNS record at your server, and the HTTPS appears — no manual certbot runs, no cron jobs to babysit renewals.

Try AODE

Self-hosted on your own VPS. One-time purchase, lifetime license. Deploy a GitHub repo with a custom domain and automatic SSL in minutes.

Related Articles

Last updated: April 2026