Skip to content
Y
YAS.SH
Docs

Operations Guide — Oracle Linux 9 Production

Operations Guide — Oracle Linux 9

This is the operating manual for the production deployment. It accompanies the deploy kit in deploy/ (setup script, systemd unit, Nginx config, backup script) and covers the operational runbook: server setup, reverse proxy, TLS, monitoring, backups, logging, and recovery.

1. Server setup

Provision a fresh Oracle Linux 9 (minimal) instance, then run:

bash deploy/setup-oracle-linux-9.sh

The script installs Node 20 LTS, pnpm 9.12.3, MariaDB 11, Nginx, Certbot, creates the unprivileged yas user and directories, generates the environment file with strong secrets, and installs the systemd unit. Never run the app as root — the unit file enforces an unprivileged user.

2. Application deployment

From a build machine:

rsync -av --exclude node_modules --exclude .next ./ yas@HOST:/opt/yas/

On the server:

sudo -u yas bash -c 'cd /opt/yas && pnpm install --frozen-lockfile && pnpm db:generate && pnpm db:push && BUILD_STANDALONE=true pnpm build'
cp -r /opt/yas/.next/standalone/. /opt/yas/
cp -r /opt/yas/.next/static /opt/yas/.next/
cp -r /opt/yas/public /opt/yas/
systemctl enable --now yas

The standalone build contains its own minimal node_modules, so the runtime depends only on the system Node binary.

3. Reverse proxy

deploy/nginx-yas.conf terminates TLS and proxies to the app on 127.0.0.1:3000. Key behaviors:

  • HTTP → HTTPS redirect
  • Security headers (HSTS, nosniff, X-Frame-Options, Referrer-Policy, Permissions-Policy)
  • /_next/static cached immutable (1 year), /blog-img cached 1 day
  • /api/ marked no-store
  • Real client IP forwarded (X-Forwarded-For, X-Real-IP) — the app's rate limiting and geo/device capture depend on these headers being set by the proxy
  • TRUST_PROXY=true must be set in /etc/yas/yas.env (the setup script already writes it) so the app trusts these headers. Nginx overwrites X-Real-IP with $remote_addr, so external callers cannot spoof it. (v3.2.5)

Validate any change with nginx -t before reloading.

4. SSL/TLS

certbot --nginx -d yas.sh -d www.yas.sh

Certbot rewrites the Nginx config with certificate paths and installs the renewal timer (systemctl list-timers | grep certbot). HSTS is already configured; after confirming the certificate works, submit yas.sh to the HSTS preload list.

5. Monitoring

Built-in:

  • GET /api/health — JSON liveness + database check + latency (cached 30s)
  • /status — human-facing status page (client polls the health endpoint every 60s)

Wire into your monitoring:

curl -s https://yas.sh/api/health
# → {"status":"ok","db":"ok","latencyMs":2,"time":"...","version":"3.0.0"}

Alert on: status != "ok", latency above budget, 5xx rate from journalctl, disk usage of /var/backups/yas.

6. Backups

Nightly via cron (as root): 0 2 * * * /opt/yas/deploy/backup.sh

The script dumps the database (single-transaction, gzipped), archives non-generated files, and optionally encrypts with AES-256 (key at /etc/yas/backup-key, created with openssl rand -hex 32 > /etc/yas/backup-key). Retention: 14 daily backups. Test a restore at least quarterly — an untested backup is a hope.

Restore:

gunzip -c yas-db-2026-08-08.sql.gz | mysql yas    # (decrypt first if .enc)
tar xzf yas-files-2026-08-08.tar.gz -C /opt/yas
systemctl restart yas

7. Logging

  • App logs: journalctl -u yas -f and /var/log/yas/yas.log (append)
  • Nginx access/error logs: /var/log/nginx/
  • Log rotation: journald handles the service; add logrotate for /var/log/yas and /var/log/nginx

8. Recovery procedures

Scenario Procedure
App down systemctl status yasjournalctl -u yas -n 50 → fix env or code → systemctl restart yas
Database down systemctl status mariadb → check /var/lib/mysql disk → restore latest backup
Corrupt data Restore DB from backup; restart app; verify /api/health
Certificate expiry certbot renew (timer handles it); check openssl x509 -enddate -noout -in /etc/letsencrypt/live/yas.sh/cert.pem
Disk full Largest growth points: DB, /var/backups/yas, logs — clean per retention
Traffic spike App scales vertically; Nginx handles concurrency; check rate-limit headers for abuse
Security incident Revoke session secret → rotate all sessions; rotate DB password; restore from pre-incident backup

9. Upgrades

  1. Pull the new source to the build machine.
  2. pnpm install --frozen-lockfile + pnpm build (standalone) locally to validate.
  3. Sync to /opt/yas, rebuild on the server, systemctl restart yas.
  4. Run the smoke suite: BASE=https://yas.sh bash scripts/smoke.sh.
  5. Watch /api/health and the status page for the first hour.
🍪 Cookies & privacy. yas.sh uses only essential cookies to keep you signed in and remember your preferences. We do not run third-party trackers. See our cookie policy and privacy policy.
Settings