# Security: how ox hardens your VPS and isolates apps How ox secures your VPS: SSH keys only, a firewall, fail2ban, each app as its own sandboxed user, signed updates, and an audit you can run any time. When you add a server, ox locks down the host before it enrolls, runs each app as its own user in a sandbox, and takes only updates signed by ox's release key. This page lists what ox sets, how to check it on your server, and what stays your job. ## What ox sets on your server - **SSH:** keys only, no root login, no passwords even when the cloud image turns them on, and weak MACs off. ox logs in again through the new settings before it keeps them, so a mistake cannot lock you out. - **Firewall:** ufw refuses incoming connections by default and opens SSH, 80 and 443. Rules already in ufw before ox, such as a port you opened yourself, stay open. - **Password guessing:** fail2ban bans addresses that keep probing SSH, for an hour at first, then 4 hours, then a day at most each time they come back. Logging in with the wrong key before the right one does not count. The firewall writes nothing to the log for the packets it blocks. - **Updates:** Ubuntu's security updates install themselves every day, and the audit checks that they ran, not only that they are switched on. A kernel or library fix loads only after a restart: the server's page says "Restart needed since" with what was updated, and `ox servers restart` or the Restart now button restarts it when you choose. ox never restarts your server on its own and does not offer Livepatch. - **The kernel:** io_uring is off for every process. This part of the kernel has a long record of privilege bugs, and no app ox runs needs it. - **A record of what ox read:** your server writes a line to its own journal each time ox reads a variable's value, saves variables, deploys (with the repository and ref it was asked to build), backs up (with the bucket), runs a command, opens a backup, restores or uses Explore. A line names the project and the keys, never a value, a query or a command. Read them with `journalctl -u ox-agent -g 'security:'`. The server also refuses more than 30 value reads a minute, and the refusal is a line too. - **Response headers:** every site sends `nosniff` and a referrer policy, and HSTS once it has a domain, each only when your app did not set its own. Static files may be framed only by the same site. There is no default Content-Security-Policy, because a wrong one breaks the app. ## How your apps are kept apart Each project runs as a system user of its own, in a systemd unit with no extra privileges. An app cannot see other users' processes, cannot load kernel modules or trace other processes, and can write only its own directories. Its ports are reachable only by its own project and Caddy, and it cannot read the cloud's metadata service, where a droplet keeps its setup data. A process set to `sandbox = "relaxed"` in [ox.toml](https://console.t54.sorv.dev/docs/config) keeps its own user, no extra privileges, the hidden processes, the kernel limits and a filter that refuses rarely used calls (swap, reboot, kernel modules, raw I/O, CPU emulation, old calls, bpf and perf), but loses the read-only system: it can write wherever its user may. Builds run as the same user with the same limits on processes, the kernel and those calls, and each build step gets a temporary folder of its own that no other project can see. None of a project's own units or build steps can write a core file, which would hold your secrets, and one project cannot use up the server's processes and threads: a project's units together stop at 40% of the host's limit (all previews together, 20%), which a project gets at its next deploy. Your [variables and secrets](https://console.t54.sorv.dev/docs/operate/variables) sit in files only root and that project can read. ## Signed updates ox updates the agent on your server between jobs, after ox's own servers ran a new build for a day without trouble, and a build that does not answer the plane is put back two minutes later. The agent updates only to a build that carries two signatures: one from the ox Cloud plane and one from ox's release key, which the ox team keeps on its own machine with one backup in a password manager, never on the plane or a server. So someone who took over the plane could not replace the ox program on a server already added with one of their own. They could still run jobs on your server as your projects' users and read your projects' variables, which the plane does to deploy, and a server added while they held the plane would get whatever install script they served. The agent talks to the plane only over HTTPS, and only outward: the plane never connects in. It refuses any request ox did not sign, or one that is old or repeated, and it answers only ox's fixed list of requests. The one that runs a command you type, `ox exec`, runs as your app's own user, in its sandbox, never as root. Only your account, with full access, can start one, and each is in your audit log. ## Check your server yourself Before a server enrolls, ox reads back what it set and refuses to go on if any row fails. Run the same check on an enrolled server at any time; it changes nothing: Security self-audit: ```sh sudo ox install --audit ``` The rows `sudo ox install --audit` prints on a server ox set up: each check, what it must be, and what the host has. The same table runs before a server enrolls, and any failed row stops the install. It prints a pass or fail row for SSH root, password and key login, the firewall, fail2ban, automatic updates, the kernel's network and crash settings (redirects, source routes, setuid core dumps, Ubuntu's crash reporter off) and the modes of ox's own folders and secret files. A server whose ox is updated also runs it itself every day, and its page shows any row that drifted with the fix. Its ox also applies its kernel settings each time it starts, so a restart cannot undo them. On a server added before these settings existed, press **Update** on its page: its ox then applies the settings below each time it starts, and a row it covers clears when you press **Recheck**. When no update is offered, restarting the server does the same, but its projects are down for about a minute. `sudo ox install --harden` does the same by hand: it turns io_uring off, lets Redis fork for its snapshots (memory overcommit), writes the rules that keep apps apart and away from the cloud's metadata services, sets the kernel's network settings, and runs the audit. It also writes the fail2ban settings and turns the firewall's logging off. It leaves SSH, the firewall's rules and updates as they are. For the newer SSH settings, copy `scripts/bootstrap.sh` and `scripts/security-rollout.sh` from the ox repository to the server and run `sudo bash security-rollout.sh --server`: it writes them, runs `ox install --harden` too, and puts the saved files back after five minutes unless you log in again from outside and run the `--confirm` command it prints. ## Where your data lives ox keeps only what it needs to run your account. Your app and its data stay on your server. These stay there: - Your code, builds and releases. - Your variables and secrets. ox shows them to you but keeps no copy. - Your databases, files and uploads. - Backups of your services. A copy goes to your own S3 bucket only if you add one. - Your app's logs. ox reads them only when you ask to see them. Logs you open, rows you browse in Explore, and backups you download pass through ox to your screen. ox does not save them. With [tested deploys](https://console.t54.sorv.dev/docs/operate/tests) on, ox also sends a pull request's text and changes, and the text of its preview's pages, to DigitalOcean's AI inference, and keeps the checks it writes and each run's results. The [FAQ](https://console.t54.sorv.dev/#faq-data) lists what ox keeps and for how long, and the [privacy policy](https://console.t54.sorv.dev/privacy) has every detail. ## What stays your job - **Your app's own code and dependencies.** The sandbox limits what a bad dependency can reach; it does not stop it from reading that project's own data. - **Who holds an SSH key.** Anyone with a key for the operator account is root on the server. - **The Add server command.** Treat it like a password until the server shows up; it expires on its own. - **Backups somewhere else.** Keep copies in your own S3 bucket, as [Backups and restore](https://console.t54.sorv.dev/docs/operate/backups) explains, and copy what no backup holds, which [What ox backs up](https://console.t54.sorv.dev/docs/operate/leaving#not-backed-up) lists. Something here looks wrong, or you found a hole? Write to the address on the [privacy page](https://console.t54.sorv.dev/privacy). If a deploy fails after a hardening change, start with [Troubleshooting](https://console.t54.sorv.dev/docs/troubleshooting), and see the [CLI reference](https://console.t54.sorv.dev/docs/cli) for the rest of `ox`. Last updated 2026-10-25. The page as HTML: https://console.t54.sorv.dev/docs/security