RackNerd VPS runbook for Shouon Al-Ghithaa — SSH from zero to multi-project ops: adding projects, staging/production isolation, zrok tunnels, domains, backups, and plan upgrades. Written for a firs...
RackNerd Server Runbook — Shouon Al-Ghithaa
Audience: someone who has never used SSH or administered an Ubuntu server, and needs to operate this specific server safely. Also written for Claude: every command is tagged, every invariant is explicit. If you are an AI agent, read §0 and §15 before running anything.
§0 — How to read this document
Command tags
Every command block carries one of these. Never run a WRITES command you do not understand.
| Tag | Meaning |
|---|---|
[READ-ONLY] | Changes nothing. Safe to run any time, any environment. Run these freely. |
[WRITES] | Changes files, services, or data. Reversible unless stated. |
[DESTRUCTIVE] | No undo. Data loss possible. Take a backup first and read the whole section. |
The five rules that matter more than any command here
- The copy decides the environment. There is no
--production/--stagingflag anywhere. Every tool reads thedeploy/.envfile sitting next to it:
So/opt/shouon → database "shouon" → PRODUCTION /opt/shouon-staging → database "shouon-staging" → STAGINGcd /opt/shouon-staging && ./deploy/shouonctl restorecannot touch production. A flag can be forgotten after you press Enter; a path is visible before you press it. - Always ask the server which environment you are in, before any command that writes. It is one read-only command (§4.1).
user adddoes not ask you — and it once wrote an admin account into the production database from the staging copy. -
shouonctl updatenever runs SQL. By design. It updates code, services and web config. It does not create a table, alter a column, or run a migration. When a database file changed in a pull,updateprints a warning line at the end of its output naming those files. That warning line is the source of truth for "what needs running" — notgit diff, not this document. - Run
updateandcheckas a pair.updatedeploys;checkmeasures. A greencheckis the only statement about the server's health that means anything. - "Not measured" is not "broken", and it is not "fine" either.
checksays!when it could not measure something (no data to probe with, for example). That is an honest third answer. Do not read it as a failure, and do not read it as a pass.
§1 — This server at a glance
All numbers below were measured on the server, not assumed.
| Provider | RackNerd (KVM VPS) |
| IP | 204.44.93.211 |
| OS | Ubuntu 24.04 LTS |
| RAM | 1.9 GB (~500 MB in use) |
| Disk | ~34 GB (~13% used) |
| Swap | none configured — see §14.6, this matters |
| Login | root over SSH on port 22 |
| Open to the internet | 22, 80, 443 only (ufw: deny incoming by default) |
| Everything else | bound to 127.0.0.1 — unreachable from outside |
What runs on it
| Service | Port | Role |
|---|---|---|
postgresql | 5432 | the database |
postgrest | 3000 | auto-generated REST API over the database |
shouon-auth | 9999 | login/session service + live event stream (SSE) |
shouon-storage | 9000 | file uploads |
caddy | 80, 443, 8088 | web server + automatic HTTPS certificates |
php8.3-fpm | — | the /api/*.php AI endpoints |
coturn | — | relay for voice/video calls behind restrictive networks |
Public entry points
| URL | Serves | Note |
|---|---|---|
https://shouon-al-ghethaa.com | production | the paid domain; clients use this |
https://www.shouon-al-ghethaa.com | → redirects | 301 only, never serves (§8.4) |
https://204.44.93.211.sslip.io | production | safety net; saved us during a 12h tunnel outage |
https://shouonalghithaa.share.zrok.io | staging | zrok tunnel, retargeted 2026-09-29 |
⚠️ The zrok link used to serve production and was given to clients. It now serves staging (an empty database). If a client reports "my account is gone", that is why — send them to the paid domain.
Where things live on disk
| Path | What |
|---|---|
/opt/shouon | production code (a git clone) |
/opt/shouon-staging | staging code (a separate git clone) |
/opt/shouon/deploy/.env | the secrets file. Not in git. Losing it is bad (§11.4) |
/etc/shouon/ | service config: postgrest.conf, auth.env, storage.env |
/var/lib/shouon/storage/ | uploaded files |
/var/backups/shouon/ | database backups |
/etc/caddy/conf.d/shouon.caddy | this project's web config |
/etc/caddy/Caddyfile | server-wide. Do not edit — regenerated on every update |
§2 — SSH from absolute zero
SSH is a text connection to the server. You type a command, the server runs it and prints the result. There is no mouse and no undo.
2.1 Connect
Windows 10/11 — open PowerShell (Start → type powershell):
ssh root@204.44.93.211
macOS / Linux — open Terminal:
ssh root@204.44.93.211
First time only, it asks:
The authenticity of host '204.44.93.211' can't be established.
ED25519 key fingerprint is SHA256:...
Are you sure you want to continue connecting (yes/no)?
Type yes and press Enter. This happens once per computer. It is the server introducing itself — not an error.
Then it asks for the password. The screen shows nothing while you type. No dots, no stars. That is normal, not a frozen terminal. Type it and press Enter.
Success looks like:
root@racknerd-xxxxx:~#
2.2 Reading the prompt
root@racknerd-xxxxx:~#
└┬─┘ └──────┬─────┘ │ │
│ │ │ └── # means root: no command will ask "are you sure?"
│ │ └──── current directory (~ = /root)
│ └──────────── the server's name
└─────────────────────── you are root (full power, no safety net)
2.3 The eight commands you actually need
# [READ-ONLY] where am I? pwd # [READ-ONLY] what is in here? ls -la # [READ-ONLY] go to the project cd /opt/shouon # [READ-ONLY] read a file, one screen at a time (q to quit) less /etc/caddy/conf.d/shouon.caddy # [READ-ONLY] is a service alive? systemctl is-active postgresql # [READ-ONLY] why did a service fail? (last 50 lines) journalctl -u shouon-auth -n 50 --no-pager # [READ-ONLY] disk and memory df -h / && free -h # leave the server exit
2.4 Things that will confuse you once
- A command printed nothing. On Unix, success is usually silent. No output is good news.
- The terminal is stuck. Press
Ctrl+Cto cancel the running command. If you are inside a file viewer, pressq. - You are inside a text editor and cannot escape. In
nano:Ctrl+X, thenNto discard. Invim: pressEsc, then type:q!and Enter. shouonctl logsappears to hang. It is a live log follower — it waits for new lines forever. PressCtrl+C. To search logs instead, see §12.2.- Arabic text looks like
????. Your terminal font/encoding. The server is fine; use Windows Terminal or iTerm2.
2.5 Stop using a password — use a key (recommended, 3 minutes)
A password can be brute-forced; fail2ban is installed but a key is strictly better.
On your own computer [WRITES - your computer only]:
ssh-keygen -t ed25519 -C "my-laptop" # press Enter 3 times to accept defaults
Copy it to the server:
# macOS / Linux ssh-copy-id root@204.44.93.211 # Windows PowerShell (no ssh-copy-id): type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh root@204.44.93.211 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Now ssh root@204.44.93.211 logs in with no password.
⚠️ Verify the key works in a second terminal window before disabling password login. Disabling passwords while your key is broken locks you out of your own server, and the only way back in is RackNerd's web console (§14.2).
§3 — The mental model: how one server holds many projects
3.1 The principle: no shared file is ever edited
| Resource | Shared | Per project |
|---|---|---|
| Caddy | /etc/caddy/Caddyfile — never edited | /etc/caddy/conf.d/.caddy |
| PostgreSQL | the service | one database + roles per project |
| Local port | — | one per project |
| systemd units | — | names prefixed with the project name |
| zrok | one account, one environment (= this machine) | one share per project |
Adding a project = creating its files. Removing it = deleting them. You never open a file that serves something else.
3.2 The request path
Browser
│ https://shouon-al-ghethaa.com
▼
Caddy :443 ──reads the "Host" header──┐
│ │
│ ┌────────────┴─────────────┬──────────────────┐
▼ ▼ ▼ ▼
conf.d/shouon.caddy conf.d/shouon-staging conf.d/project2 (unknown name)
│ .caddy .caddy │
│ ▼
├── / → static files from /opt/shouon (index.html) no certificate
├── /rest/v1/* → 127.0.0.1:3000 (PostgREST) → TLS fails
├── /auth/v1/* → 127.0.0.1:9999 (auth + SSE) ERR_SSL_
├── /storage/v1/*→ 127.0.0.1:9000 (files) PROTOCOL_ERROR
└── /api/*.php → php8.3-fpm
3.3 Port registry — reserve before you use
| Port | Owner |
|---|---|
| 3000 / 9999 / 9000 | Shouon production: REST / auth / storage |
| 8088 | Shouon production — local tunnel entry |
| 3001 / 9998 / 9001 | Shouon staging |
| 8089 | Shouon staging — local tunnel entry |
| 8090 and up | next projects |
| 5432 | PostgreSQL (all projects share the service) |
| 2019 | Caddy admin API |
[READ-ONLY] What is actually listening right now:
ss -tlnp | awk '{print $4}' | grep -oE '[0-9]+