Public-cloud deployment of a single-user expense tracker: - 4-container stack: Caddy (HTTPS via Let's Encrypt), nginx (React/Vite SPA), Express API, Postgres 16 - Repeatable deployment via prepare-app.sh using only OCI CLI (no web console) - Persistent Postgres volume, auto-restart policies, healthchecks, backup + restore scripts - DuckDNS dynamic DNS for the public hostname; secrets isolated to gitignored .env Author: Gigi Saji Live URL: https://savesave.duckdns.org
2.3 KiB
2.3 KiB
DuckDNS Setup (for HTTPS on sk1)
What DuckDNS is
DuckDNS is a free, donation-funded dynamic DNS service. It gives you a subdomain under duckdns.org and an HTTP API to point it at any IP.
| Feature | Free? |
|---|---|
| 5 subdomains per account | ✅ |
| Unlimited updates via HTTP API | ✅ |
| IPv4 and IPv6 records | ✅ |
| Custom domains | ❌ (use a registrar instead) |
| Email / MX records | ❌ |
| Premium tier | does not exist |
No credit card. No expiration. Forever free.
Why this project uses it
prepare-app.sh needs to point a DNS name at the new VM's public IP before Caddy starts, so Let's Encrypt's HTTP-01 challenge succeeds on the first try. DuckDNS is the simplest free option that has a scriptable update URL.
One-time setup (90 seconds)
- Go to https://www.duckdns.org/
- Click "sign in with GitHub" (or Google / Twitter / Reddit)
- In the "domains" box, type a name (e.g.
gigi-expenses) and click "add domain" → your full hostname is nowgigi-expenses.duckdns.org - At the top of the page, copy the token (a UUID like
abcd1234-…)
Put it in sk1/.env
DUCKDNS_DOMAIN=gigi-expenses.duckdns.org
DUCKDNS_TOKEN=abcd1234-aaaa-bbbb-cccc-1234567890ab
LE_EMAIL=you@example.com
That's it. prepare-app.sh will call the DuckDNS update API with these values during the deploy.
Manual test (optional)
To verify the token works before running prepare-app.sh:
curl "https://www.duckdns.org/update?domains=gigi-expenses&token=YOUR_TOKEN&ip="
# should print: OK
(Leaving ip= empty makes DuckDNS auto-detect your current IP — useful for the sanity check; the real deploy passes the VM's IP explicitly.)
Troubleshooting
| Response | Meaning | Fix |
|---|---|---|
OK |
Worked | — |
KO |
Wrong token or domain | Double-check both values; tokens are case-sensitive |
| timeout | Network/DNS issue on your side | Try again, or use a different DNS resolver |
Limits worth knowing
- DNS propagation: usually < 30 seconds globally; sometimes up to a few minutes
- Let's Encrypt rate limit: 50 certs / week / registered domain; not a concern here since you'll only issue 1
- DuckDNS API rate limit: generous, not documented; nowhere near the few calls
prepare-app.shmakes