Cubis Engineers

Services and networking

Run applications with systemd and trace requests across DNS, sockets, firewalls, and HTTP.

Cloud & infrastructureIntermediateUpdated Aug 13, 2026systemdnetworkingdnsportsprocesses

Most production failures become tractable when you can answer two questions: what process should be running? and how should traffic reach it?

Inspect processes and resources

Terminal
ps aux --sort=-%mem | head
pgrep -af 'node|python|java'
top                               # press 1 for per-CPU view
systemctl --failed
kill -TERM 1234                   # request graceful shutdown

Prefer SIGTERM and wait for graceful shutdown. Use SIGKILL only when the process cannot respond; it prevents cleanup and can leave state inconsistent.

Create a systemd service

/etc/systemd/system/cubis-api.service
[Unit]
Description=Cubis API
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/srv/cubis-api/current
EnvironmentFile=/etc/cubis-api.env
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Keep secrets in a root-owned file such as /etc/cubis-api.env with mode 600; do not store them in the unit or repository.

Terminal
sudo systemd-analyze verify /etc/systemd/system/cubis-api.service
sudo systemctl daemon-reload
sudo systemctl enable --now cubis-api
systemctl status cubis-api --no-pager
journalctl -u cubis-api -n 100 --no-pager

After changing application code, restart the service. After changing only the unit, run daemon-reload first.

Understand listening sockets

Terminal
sudo ss -tulpn                  # TCP/UDP listeners and owning processes
sudo ss -ltnp 'sport = :3000'
curl -fsS http://127.0.0.1:3000/health

Binding to 127.0.0.1:3000 means only local processes can connect. Binding to 0.0.0.0:3000 exposes the socket on every IPv4 interface if the firewall allows it. An application behind Nginx should normally bind to loopback.

Trace the request path

Follow the layers in order instead of guessing:

Resolve DNS

dig +short app.example.com A should return the expected public IP. Also inspect AAAA if IPv6 is published.

Reach the host

nc -vz app.example.com 443 checks TCP reachability. A timeout suggests routing or firewall; refusal means the host answered but nothing accepted the port.

Negotiate TLS and HTTP

curl -vI https://app.example.com exposes DNS, connection, certificate, protocol, redirect, and response headers.

Reach the application locally

On the server, curl -v http://127.0.0.1:3000/health. If local works but public fails, focus on Nginx, TLS, or the firewall.

Correlate logs

Read Nginx access/error logs and the service journal for the same timestamp or request ID.

DNS and route tools

CommandUse
dig +short name AResolve IPv4 records
resolvectl query nameQuery through the host resolver
ip -brief addressShow interface addresses
ip routeShow routing decisions and default gateway
tracepath hostDiscover path and MTU issues
curl -w '%{http_code} %{time_total}\n'Measure HTTP result and total time

Diagnostic habit State the failing layer: “DNS resolves, TCP 443 connects, TLS succeeds, Nginx returns 502, and the local health check refuses port 3000.” That sentence is far more actionable than “the server is down.”

On this page