How to put a real domain in front of a LoopCheck deployment: a public landing page, the app itself over HTTPS, a first superuser, and outbound SMTP for the admin/office account emails.
This sits on top of DEPLOY.md (the base VPS install, the
pocketbase systemd service, backups, and hardening). Placeholders below —
example.com, app.example.com, <droplet-ip>, you@example.com,
<server>.mxrouting.net — are the only things you substitute.
A single domain fans out across three names plus mail. The website records (A/AAAA) and the mail records (MX) are different record types on the same domain — they coexist with zero conflict.
| Name | Points to | Serves |
|---|---|---|
example.com (apex) |
GitHub Pages | the static landing page (docs/) |
www.example.com |
GitHub Pages | redirect to the apex |
app.example.com |
your VPS (<droplet-ip>) |
the PocketBase app, over HTTPS |
@example.com mail |
your SMTP provider | outbound account email |
The landing page (docs/index.html) is static and needs no server.
- CNAME file in the Pages publishing source (
/docs): a filedocs/CNAMEcontaining one line —example.com. - DNS at your registrar — the apex needs GitHub's four Pages A records
(add the AAAA set too for IPv6):
(If your registrar supports ALIAS/ANAME/CNAME-flattening, an
@ A 185.199.108.153 @ A 185.199.109.153 @ A 185.199.110.153 @ A 185.199.111.153 www CNAME <your-github-user>.github.io.ALIAS @ -> <your-github-user>.github.iois tidier than the four A records.) - Repo → Settings → Pages → Custom domain =
example.com, wait for the DNS check, then enable Enforce HTTPS. GitHub provisions the cert.
The app is pocketbase serve on 127.0.0.1:8090, fronted by Caddy, which
fetches and renews a Let's Encrypt certificate automatically. The frontend
talks to window.location.origin, so nothing in the app needs the hostname
hardcoded — it just works once served at https://app.example.com.
- DNS:
app A <droplet-ip>(and AAAA if you have IPv6). - Firewall: open HTTPS —
ufwmust allow 443 as well as 80 (80 is needed for the ACME challenge and the HTTP→HTTPS redirect):ufw allow 80/tcp ufw allow 443/tcp
- Caddyfile (
/etc/caddy/Caddyfile) — point it at the hostname; that single change is what triggers automatic TLS:app.example.com { reverse_proxy 127.0.0.1:8090 } - Reload:
systemctl reload caddy(it validates first and keeps the running config if the new one is invalid). Caddy issues the cert on the first HTTPS request. Verify:curl -s https://app.example.com/api/health # {"...healthy..."} curl -sI http://app.example.com/api/health | grep -i location # 308 -> https
- QR labels: set the label Base URL to
https://app.example.comso printed QR codes encode the public address.
A named Caddy site (
app.example.com { ... }) only answers that host, sohttp://<droplet-ip>stops serving the app once you switch from a:80block — that's the intended end state. Keep a/etc/caddy/Caddyfile.bakfor an easy revert.
The admin UI (/_/ → Settings, where SMTP lives) requires a
superuser. On a public instance PocketBase will not let anyone
create the first one through the browser without a one-time install token
(printed at startup, valid ~30 min) — so a stranger can't grab your admin.
Create it from the CLI instead — no token, works headless. Run it as the
service user so it targets the right pb_data:
cd /opt/loopcheck/app
sudo -u loopcheck ./pocketbase superuser upsert you@example.com "a-strong-password"upsert creates the account, or resets the password if the email already
exists. Then log in at https://app.example.com/_/.
Gotchas seen in the wild:
- "Only superusers can perform this action" in the browser = you're on a
stale installer link (
/_/#/pbinstall/…) while a superuser already exists. Go to the plain/_/— it lands on the normal login. - A blank/
404installer link = the printed token expired. Don't chase it; use the CLIupsertabove.
SMTP powers PocketBase's password-reset, email-verification, and OTP mail for superuser/office accounts. The accountless field tier never sends mail, so this is only for the people who log in.
Admin UI → Settings → Mail settings → Use SMTP mail server:
| Field | Value |
|---|---|
| SMTP host | your provider's server, e.g. <server>.mxrouting.net |
| Port / TLS | 587 StartTLS, or 465 SSL |
| Username | the full mailbox address, e.g. no-reply@example.com |
| Password | that mailbox's password |
| Sender address / name | no-reply@example.com / LoopCheck |
Then use Send test email — that's the instant pass/fail.
The sending domain needs three records, whose exact values your provider
generates for example.com:
- SPF —
TXT @— thev=spf1 include:… -allline the provider gives. - DKIM — enable it in the provider's panel and add the selector record.
- DMARC —
TXT _dmarc— e.g.v=DMARC1; p=quarantine; rua=mailto:you@example.com.
mxroute note: a domain must be added to your mxroute account (DirectAdmin → add domain → create the mailbox) before
no-reply@example.comexists there. Your mxroute server hostname is shown in the client area / DirectAdmin panel URL (<server>.mxrouting.net), not necessarily in your domain's current MX — a domain still on a registrar's free email forwarding has MX pointing elsewhere. To send only (not receive) through mxroute, you can leave inbound MX as-is and just add the SPF/DKIM records above; or, for a quick start, send from a mailbox on a domain you already have live on mxroute.