Operations
Deployment
dihlabs.lol runs on one $1.50 VPS: 1 vCPU, 2 GB of memory, Ubuntu 24.04. nginx in front, the site on Bun, the Rust engine next to it, all under systemd, updated from this…
On this page
dihlabs.lol runs on one $1.50 VPS: 1 vCPU, 2 GB of memory, Ubuntu 24.04. nginx in front, the site on Bun, the Rust engine next to it, all under systemd, updated from this repository automatically.
Production layout
| piece | where | runs as |
|---|---|---|
| nginx | :80 (and :443 with TLS) | www-data |
The site (optz start) | 127.0.0.1:3001, from /opt/optz/current/web/portfolio | optz-web.service |
| The Rust engine | 127.0.0.1:3000 | optz.service |
| Releases | /opt/optz/releases/<sha>, /opt/optz/current → the live one | |
| Data and backups | /var/lib/optz (SQLite), hourly verified snapshots | optz-backup.timer |
| Secrets | /etc/optz/env | root only |
First install
ssh root@your-server
# clone into /srv/optz and pull the branch every 30 s (BRANCH defaults to main)
curl -fsSL https://raw.githubusercontent.com/sxnistxr/OPTZ/main/deploy/setup-vps.sh | sudo BRANCH=main bash
sudo bash /srv/optz/ops/install-prod.sh # users, toolchains (pinned, checksummed), units, first deploy
install-prod.sh installs Bun 1.4.4 and the Rust toolchain from pinned, checksummed archives,
creates the optz user and directories, writes /etc/optz/env with a random secret, installs
the systemd units and runs the first deploy.
Every deploy
ops/deploy.sh builds the commit, switches releases and health-checks them; it rolls back by
itself if the new release isn't healthy within 30 seconds, and keeps the five newest releases.
Follow it with journalctl -u optz-sync.service -f.
The site build downloads its fonts from Google at build time and renders Open Graph images, so
the server needs outbound HTTPS while building (not while serving). Downloads are cached in
~/.cache/optz, so later builds only fetch what changed.
Behind Cloudflare
The site sends Cache-Control: s-maxage=300, stale-while-revalidate=86400 on pages
(server.cdnMaxAge in optz.config.ts), so a CDN in front serves them for five minutes and
refreshes in the background. To clear Cloudflare's copy on every deploy, add a zone id and an API
token with the "Cache Purge" permission to /etc/optz/env:
CF_ZONE_ID=...
CF_API_TOKEN=...
deploy.sh purges the zone after the new release is live. Without them it skips the purge, and
pages update at the CDN within five minutes.
The domain and TLS
Point dihlabs.lol (and www) at the server with A records, then get a certificate. Use
certonly: the nginx config is generated on every deploy, so certbot shouldn't edit it.
sudo apt install certbot python3-certbot-nginx
sudo certbot certonly --nginx -d dihlabs.lol -d www.dihlabs.lol --deploy-hook "systemctl reload nginx"
sudo bash /srv/optz/ops/deploy.sh # regenerates nginx with HTTPS; "already live" is fine
Once /etc/letsencrypt/live/<domain>/ exists, every deploy generates the config with HTTPS
(HTTP/2, HSTS) and a redirect from port 80. certbot renews the certificate by itself and the
deploy hook reloads nginx. The site's absolute URLs (canonical links, Open Graph, sitemap) come
from url in portfolio/content/site.json, or the SITE_URL environment variable.
nginx
The config is generated, not hand-written: optz nginx turns optz.config.ts into a server
block (hashed assets from disk with gzip_static, everything else to Bun with keep-alive, and
micro-caching for proxied APIs). The deploy regenerates and reloads it when it changes.
Other ways to run an optz app
| command | notes | |
|---|---|---|
| Single executable | optz build --compile → dist/server | copy one file and run it; sharp is used if it can be found |
| Edge worker | optz build --target edge → dist/edge/ | npx wrangler deploy from there (Cloudflare), or Deno Deploy / Vercel Edge |
| Static host | optz export → dist/static | no server features |
Operations
- Logs:
journalctl -u optz -u optz-web -f - Restart:
sudo systemctl restart optz optz-web - Memory per connection on the box:
bash /srv/optz/ops/vps-mem.sh - Restore a backup:
ops/RESTORE.md