| name | server-admin |
| description | Administer Ronak's VPS at 5.78.176.171, including Docker Compose stacks, the Tailscale-through-WireGuard exit node, Caddy, the ronakpjain.com website, BlueMap, Minecraft/Purpur, DNS/routing checks, logs, firewall, users, SSH, and general server maintenance. |
Server Admin
Server facts
- Host:
5.78.176.171
- SSH user:
ronak
- Local SSH alias:
ronak-vps (configured in /Users/ronak/.ssh/config).
- Agent forwarding is enabled only for
ronak-vps / 5.78.176.171; this lets the VPS access Ronak's private GitHub repositories without copying the private key.
- Root SSH is disabled. SSH is key-only (
PasswordAuthentication no), X11 forwarding is disabled, and MaxAuthTries is 3 via /etc/ssh/sshd_config.d/10-hardening.conf.
ronak has passwordless sudo and is in the docker group.
unattended-upgrades automatically installs both security and normal Ubuntu updates while respecting phased rollout. Automatic rebooting is disabled; reboot manually when /var/run/reboot-required exists.
- Old server
167.99.14.44 was shut down after migration.
Paths
- Minecraft/Purpur compose project:
/home/ronak/minecraft/purpur
- Minecraft data volume:
/home/ronak/minecraft/purpur/data
- Website compose project:
/home/ronak/web
- Website app data/build:
/home/ronak/web/build
- Website Unix socket dir:
/home/ronak/web/socket
- Caddy compose project:
/home/ronak/caddy
- Caddyfile:
/home/ronak/caddy/Caddyfile
- Caddy data/config:
/home/ronak/caddy/caddy_data, /home/ronak/caddy/caddy_config
- Uptime Kuma compose project:
/home/ronak/uptime-kuma
- Uptime Kuma data:
/home/ronak/uptime-kuma/data
- Tailscale/WireGuard compose project and Git checkout:
/home/ronak/tailscale-wireguard-exit-node
- Private source repository:
git@github.com:ronakpjain/tailscale-wireguard-exit-node.git
- Local-only WireGuard profile manifest:
/home/ronak/tailscale-wireguard-exit-node/config/profiles.json
- Local-only WireGuard configs:
/home/ronak/tailscale-wireguard-exit-node/config/wireguard/*.conf
- Stopped legacy exit-node project:
/home/ronak/tailscale-airvpn (do not start alongside the active stack; both use UDP 41641)
- Migration rollback copy:
/home/ronak/backups/tailscale-airvpn-20260730-212611
- Persistent host firewall:
/etc/nftables.conf (inet host_firewall); pre-hardening copy: /etc/nftables.conf.pre-hardening
- Exit-node watchdog:
/usr/local/sbin/tailscale-wireguard-watchdog, /etc/systemd/system/tailscale-wireguard-watchdog.service, and .timer
- Docker daemon config:
/etc/docker/daemon.json
- Automatic update policy:
/etc/apt/apt.conf.d/20auto-upgrades, /etc/apt/apt.conf.d/50unattended-upgrades, and /etc/apt/apt.conf.d/52unattended-upgrades-local
- Pre-change unattended-upgrades backup:
/root/config-backups/50unattended-upgrades.pre-all-updates-20260730
- Chat export:
/home/ronak/minecraft/purpur/chat-log-formatted.txt
Containers
Expected containers:
purpur from itzg/minecraft-server:latest
caddy from caddy:latest
ronfire from ironic06/ronfire:latest
uptime-kuma from louislam/uptime-kuma:latest
tailscale-wireguard-gateway from the locally built repository gateway image
tailscale-wireguard from tailscale/tailscale:stable
Minecraft compose exposes:
- Java:
25565/tcp
- Bedrock/Geyser:
19132/udp
- BlueMap is not directly exposed; it is proxied internally by Caddy to
purpur:8100.
- Uptime Kuma is not directly exposed; it is proxied internally by Caddy to
uptime-kuma:3001.
Caddy exposes:
- HTTP:
80/tcp
- HTTPS:
443/tcp
Docker/network wiring:
caddynet external network connects Caddy and Purpur so Caddy can reach purpur:8100.
- Website and Caddy are intentionally separate compose projects.
ronfire writes/serves via Unix socket at /home/ronak/web/socket/website.sock.
- Caddy mounts
/home/ronak/web/socket:/app/socket and proxies ronakpjain.com to unix//app/socket/website.sock.
- Uptime Kuma joins the external
caddynet network so Caddy can reach uptime-kuma:3001.
tailscale-wireguard shares the gateway container's network namespace (network_mode: service:gateway).
- Tailnet traffic arriving on
tailscale0 is policy-routed through WireGuard interface wg-exit using table 51820.
- An early nftables hook named
wireguard_killswitch rejects forwarded Tailscale traffic unless its output interface is wg-exit, covering IPv4 and IPv6 fallback failures.
- Only Tailscale UDP
41641 is published by this stack. The profile selector on TCP 8080 is reachable over Tailscale/loopback, not the public interface.
- Compose volumes
tailscale-wireguard-exit-node_tailscale-state and tailscale-wireguard-exit-node_wireguard-state preserve the Tailscale node identity and selected profile.
- Persistent nftables input/forward filtering allows only public TCP
22, 80, 443, 25565; UDP 19132, 41641; DHCP; established traffic; and ICMP/ICMPv6. The firewall runs before Docker's filter chains without flushing Docker-owned tables.
- Docker's global
json-file logging defaults are max-size=10m, max-file=3, compress=true. All current containers were recreated after this setting was loaded.
Domains
- Website:
ronakpjain.com, www.ronakpjain.com
- BlueMap:
bluemap.ronakpjain.com
- Uptime Kuma admin:
uptime.ronakpjain.com
- Public status page:
status.ronakpjain.com (also available at uptime.ronakpjain.com/status/status)
- Minecraft domain should be a DNS-only/gray-cloud record such as
mc.ronakpjain.com -> 5.78.176.171 because Cloudflare orange-cloud does not proxy standard Minecraft TCP/UDP.
Caddy BlueMap block should normally be:
bluemap.ronakpjain.com {
reverse_proxy purpur:8100
}
Caddy Uptime Kuma blocks should normally be:
uptime.ronakpjain.com {
reverse_proxy uptime-kuma:3001
}
status.ronakpjain.com {
reverse_proxy uptime-kuma:3001
}
Current Minecraft features/plugins
- Purpur, Minecraft internal version shown as
26.1.2.
- Plugins:
- BlueMap
- Geyser-Spigot
- floodgate
- OnePlayerSleep
- Geyser/Floodgate auth type should be
floodgate.
- Floodgate Bedrock player names use prefix
. in Java-side whitelist, e.g. .hssongg.
- OnePlayerSleep is installed so one player can sleep through night.
Common commands
Always SSH as ronak. Prefer the alias, which forwards the local SSH agent for private Git operations:
ssh ronak-vps
ssh ronak@5.78.176.171
The forwarded agent exists only during the SSH session. Never copy Ronak's GitHub private key or personal access token onto the VPS.
Minecraft:
cd /home/ronak/minecraft/purpur
docker compose ps
docker compose logs -f purpur
docker compose restart purpur
docker exec purpur rcon-cli "whitelist list"
docker exec purpur rcon-cli "bluemap maps"
Website app:
cd /home/ronak/web
docker compose ps
docker compose logs -f ronfire
docker compose restart ronfire
Caddy:
cd /home/ronak/caddy
docker compose ps
docker compose logs -f caddy
docker compose restart caddy
Uptime Kuma:
cd /home/ronak/uptime-kuma
docker compose ps
docker compose logs -f uptime-kuma
docker compose restart uptime-kuma
docker exec uptime-kuma sqlite3 /app/data/kuma.db "select id,name,type,url,hostname,port,active from monitor order by id;"
Add Uptime Kuma monitors directly via SQLite when needed. Use the UI if convenient, but for agent automation this pattern works:
cd /home/ronak/uptime-kuma
docker exec -i uptime-kuma sqlite3 /app/data/kuma.db <<'SQL'
INSERT INTO monitor (name, active, user_id, interval, url, type, maxretries, retry_interval, maxredirects, method, timeout)
SELECT 'Service Name', 1, 1, 60, 'https://example.com', 'http', 1, 60, 10, 'GET', 48
WHERE NOT EXISTS (SELECT 1 FROM monitor WHERE name='Service Name');
SQL
docker compose restart uptime-kuma
cd /home/ronak/uptime-kuma
docker exec -i uptime-kuma sqlite3 /app/data/kuma.db <<'SQL'
INSERT INTO monitor (name, active, user_id, interval, type, hostname, port, maxretries, retry_interval, timeout)
SELECT 'Service Port', 1, 1, 60, 'port', '5.78.176.171', 25565, 1, 60, 48
WHERE NOT EXISTS (SELECT 1 FROM monitor WHERE name='Service Port');
SQL
docker compose restart uptime-kuma
Add an existing monitor to the public status page:
docker exec -i uptime-kuma sqlite3 /app/data/kuma.db <<'SQL'
INSERT OR IGNORE INTO monitor_group (monitor_id, group_id, weight, send_url)
SELECT m.id, g.id, m.id, 0
FROM monitor m
JOIN `group` g ON g.name='Services'
JOIN status_page s ON s.id=g.status_page_id AND s.slug='status'
WHERE m.name='Service Name';
SQL
docker compose restart uptime-kuma
Current public status page monitors are Website, Website WWW, BlueMap, Uptime Kuma, and Minecraft Java.
Tailscale/WireGuard exit node:
cd /home/ronak/tailscale-wireguard-exit-node
docker compose ps
docker compose logs -f gateway tailscale
docker exec tailscale-wireguard tailscale status
docker exec tailscale-wireguard-gateway wg show wg-exit
docker exec tailscale-wireguard-gateway wireguard-switch health
systemctl status tailscale-wireguard-watchdog.timer
sudo systemctl start tailscale-wireguard-watchdog.service
The watchdog runs about every two minutes. Docker's health check already requires three consecutive failures; if the existing gateway is unhealthy or either existing container is not running, the watchdog recreates both services. If both containers are absent after an intentional docker compose down, it does nothing.
Update from the private repository (requires an agent-forwarded SSH session):
cd /home/ronak/tailscale-wireguard-exit-node
git pull --ff-only
docker compose up -d --build
Current local profiles are us and chicago; chicago was selected after migration. Switch from the CLI with rollback-on-failure validation:
docker exec tailscale-wireguard-gateway wireguard-switch switch us
docker exec tailscale-wireguard-gateway wireguard-switch switch chicago
The selector is available inside the tailnet through MagicDNS:
http://hetzner-airvpn-exit:8080
Useful selector/status checks:
curl -sS http://hetzner-airvpn-exit:8080/api/status
docker exec tailscale-wireguard-gateway wget -qO- http://127.0.0.1:8080/api/status
Tailnet DNS currently has MagicDNS enabled, global Google resolvers, and local-DNS override enabled. Clients should use Tailscale DNS (tailscale set --accept-dns=true). Real profiles.json, .env, and WireGuard .conf files are ignored by Git and must never be committed or printed in logs.
Health checks:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
curl -sS https://bluemap.ronakpjain.com -o /tmp/bm.html -w "%{http_code} %{size_download}\n"
curl -sS -I https://uptime.ronakpjain.com
curl -sS -I https://status.ronakpjain.com
curl -sS -I -H "Host: bluemap.ronakpjain.com" http://127.0.0.1
Note: BlueMap returns 400 to some HEAD requests; verify with normal GET when in doubt.
Whitelist notes
Main whitelist is enabled. Known whitelist entries included:
Ironic5000
bovabova1
Claydunn
.hssongg
.PurdueChemE
KittyScanArchive was removed. szvi_ was intentionally excluded.
Floodgate command exists:
docker exec purpur rcon-cli "fwhitelist add hssongg"
docker exec purpur rcon-cli "fwhitelist add PurdueChemE"
BlueMap rendering
BlueMap maps:
overworld
world (Nether)
world_the_end
Flat, perspective, free-flight/3D, and hires were enabled in generated configs.
Render/status commands:
docker exec purpur rcon-cli "bluemap"
docker exec purpur rcon-cli "bluemap maps"
docker exec purpur rcon-cli "bluemap tasks"
docker exec purpur rcon-cli "bluemap start"
docker exec purpur rcon-cli "bluemap update overworld"
docker exec purpur rcon-cli "bluemap update world"
docker exec purpur rcon-cli "bluemap update world_the_end"
Safety/guardrails
- Before risky changes, check container state and paths.
- For data migration or volume operations, stop affected containers first.
- Avoid logging secrets/passwords.
- Do not re-enable root SSH unless explicitly requested.
- If touching Caddy certs/DNS, check Caddy logs and Cloudflare proxy behavior.
- Before exit-node changes, verify the selected profile, save a rollback path, and remember that restarting it briefly interrupts connected clients.
- Never run
/home/ronak/tailscale-airvpn at the same time as /home/ronak/tailscale-wireguard-exit-node; they conflict on container purpose and UDP port 41641.
- Preserve and do not recreate the Tailscale identity volume unless intentionally registering a new tailnet node.
- Do not display, copy into Git, or expose
config/wireguard/*.conf; they contain private WireGuard keys.
- For private-repo pulls, use agent forwarding from a trusted local machine. Do not install Ronak's personal private key on the server.
- After exit-node updates, validate gateway health, Tailscale identity
100.73.123.109, selector status, IPv4/IPv6 egress, DNS, and the wireguard_killswitch table.
- Keep
nftables.service enabled. When changing /etc/nftables.conf, do not flush the complete ruleset while Docker is running; Docker owns separate nftables/iptables-nft tables. Validate with sudo nft -c -f /etc/nftables.conf and preserve a working SSH session until a fresh login succeeds.
- Before planned exit-node maintenance, optionally stop
tailscale-wireguard-watchdog.timer to prevent automatic recovery from racing the maintenance, then start it afterward.
- Docker log defaults only apply when a container is created. Recreate containers after changing
/etc/docker/daemon.json and verify HostConfig.LogConfig.Config.
- Use
sudo from ronak if OS-level admin is needed.