Built with Chinaski

Port Conflicts

Port collisions between Chinaski instances (or between Chinaski and other software) show up as a service that won’t start, an admin UI that never answers, or a proxy that reports the backend unreachable. This page explains which ports Chinaski uses by default, where the port actually gets chosen, and how to diagnose and fix the usual conflicts.

Which port is which

Chinaski has a few different “ports” in play, and they belong to different layers:

  • The CMS daemon listens on the address in /etc/chinaski/env (LISTEN_URL), which is the only place it’s set at runtime for a systemd deployment. With the standard installer, the instance port is derived automatically: numeric names get 3000 + N (instance 2 → 3002), non-numeric names get 3001; pass --port to override.
  • Quick installs / offline bundles (no reverse proxy, hypnotoad direct) listen on 8080 by default, on all interfaces until you narrow it.
  • Caddy or nginx own 80/443 and reverse-proxy to the per-instance loopback port.
  • The MCP server binds no port at all: it connects out to the CMS’s URL; only its CHINASKI_API_URL references a port.

Common symptoms and causes

The service unit flaps or fails shortly after start. Something else already holds the port. After five failed starts within a minute the unit stops retrying and enters a failed state: systemctl status chinaski@ plus the journal shows the story. Check who has the port with ss -tlnp | grep :300 (the production checklist’s own diagnostic). Fix by moving either party: set a different LISTEN_URL in the instance’s env file, or stop/repurpose the conflicting listener before restarting.

The admin or public site answers 502-style errors after a port change. The proxy no longer matches the backend. The generated Caddy example names this explicitly: its upstream address must equal the bind address in the instance’s env file. Change one side and not the other (e.g. you edit /etc/chinaski1/env to a new port but not the Caddyfile), and the proxy keeps dialing the old port. Fix: edit both the env file and the proxy upstream, then restart the CMS and reload the proxy. Verify with ss -tlnp | grep :300 that the daemon is now bound, and with journalctl -u chinaski@ that it stayed up.

Two instances collide after a manual re-install. If the second instance was installed without letting the installer pick a port (e.g. a non-numeric name or a reused --port), it shares the first’s port. Each instance needs its own port; the Caddyfile example spells out the expected allocation (instance 1 → 3001, instance 2 → 3002 …) so each new instance should land on the next free 3000-series port.

The site listens but nobody outside can reach it. Not a conflict at all: production installs bind the CMS to loopback on purpose (http://127.0.0.1:3001), with the reverse proxy accepting the world’s traffic. Widen the bind to 0.0.0.0 and it will “start working”, while also letting any client spoof forwarded headers and defeat IP-based rate limiting and login lockouts. The correct fix for remote access is to proxy through Caddy/nginx, not to widen the bind.

Changing ports cleanly

  1. Decide the new port per instance and update LISTEN_URL in /etc/chinaski/env.
  2. Update the matching proxy upstream (127.0.0.1:) on the Caddy/nginx side.
  3. Restart the CMS unit and reload or restart the proxy.
  4. If the new port needs firewall access for a non-proxy edge case, mirror the firewall rule to the same port: the installer’s own guidance routes every language vhost’s traffic to that one port.

Remember that the production units run the daemon as the dedicated chinaski user with a hardened sandbox, so pick high ports (like the 3000-series convention above) for the CMS backend rather than system-level ports.

A quick diagnosis recipe

systemctl status chinaski@<N>            # is the unit in a restart loop / failed?
journalctl -u chinaski@<N> --since today # what did the daemon say at startup?
ss -tlnp | grep -E ':(3000|3001|3002|8080)'   # who owns which port now?

If the expected port is held by the right process but the proxy still errors, compare the proxy’s upstream line against the env file’s LISTEN_URL: a mismatch there is the single most common cause of “backend running but site down”.

Read this site in