BlogProduct

Self-Hosted Deploy Tools Felt Too Busy, So I Built ox

I could pay a lot to keep deploys simple, or pay less and fight a busy screen. I wanted neither, so I built a calm deploy tool for my own server.

I built ox because putting my apps online was always the hard part, and no tool felt right to me. The easy hosted tools cost a lot. The tools you run on your own server were good, but their screens had too much on them. So I made a small tool that puts a repo on your own server, and I kept every screen as calm as I could.

In this guide:

Writing the app was the fun part

I taught myself to code on freeCodeCamp. Then I spent about five years building the server side of web apps for clients. Writing the app was always the fun part for me. Getting it online was not.

Each time, I had to set up a server again. The web server. The database. The jobs that run on a timer. I wrote long guides about it on this blog, like the one for Django with Gunicorn, Nginx and PostgreSQL. Each guide had many steps.

Two roads, and I did not like either

First I tried the easy tools, like Vercel. You push your code, and it goes live. It is a lovely feeling. But that nice feel costs a lot of money.

Then I tried tools you run on your own server. They are wonderful tools, and many people love them. I mean that. But their screens had too much on them for me. I could not enjoy using them.

So I could pay a lot to keep things simple. Or I could pay less and fight a busy screen. I did not want either one.

So I built my own deploy tool

I called it ox. It puts your app on a server you rent, called a VPS. You push your code to GitHub. ox builds it and puts it live on your server. It does not use Docker. It uses the plain parts that come with Linux. Each app runs as a systemd service, and Caddy sits in front of it with HTTPS.

I set one rule for the screens. Show what you need right now, and hide the rest. A project's main page shows if your app is up, its web address and the last deploy. Everything else sits behind Settings. A new app needs no setup at all, because ox picks good settings for you.

Most of what ox does comes from pain I felt over the years. Here are the small things I cared about most.

Logs that show what led to an error

When something breaks, I filter the logs to show only errors. That helps. But a filter also hides the lines just before the error, and those lines often say why it happened.

So in ox, when a filter is on, each matching line gets two buttons. One brings back the 10 lines just above it. The other brings back the 10 lines just below it. Press again to go 10 lines further. You see what led up to the error, not only the error.

You can filter from a terminal too:

Shell
ox logs shop --severity error
ox logs shop --process web --grep "timeout"

The logs page in the docs shows every filter.

Your AI writes the setup file

Most apps need no setup file. When yours does, it is one small file called ox.toml. You do not have to write it by hand. You copy our guide into your coding AI. It reads your code and writes the file for you.

Here is the part I care about. The AI only writes the file. When you deploy, ox just reads it. So the same file gives the same result every time. Here is one for a Django app with a worker:

TOML
domains = ["myapp.com"]

[app]
health = "/healthz"

[workers]
worker = "celery -A app worker"

[services]
postgres = {}
redis    = {}

Every key you can use is in the config reference.

A check before every deploy

ox checks the file before it changes anything. A mistake, or a secret you forgot to set, stops the update before your live site is touched. You can run the same check in your repo:

Shell
ox check

It prints the plan and the variables you still need. When all is well, it ends with "Ready to deploy."

A failed update leaves your site up

This one matters most to me. If a step fails, your live app keeps running. The new version starts next to the old one. Traffic moves only after the new one passes its health check. If a step fails, your visitors stay on the old version. ox tells you which step failed, why, and how to fix it.

Commands for you and your AI

Most things on the dashboard are also a command. Each command can print its answer as data with --json, so an AI agent can use it too:

Shell
ox deploy myapp --wait --json

The CLI reference lists them all.

Where ox stands today

ox is in beta, and my own apps run on it. One of them is Lazy Planner, a small app that helps you plan your days. ox works with JavaScript and TypeScript, Python, Go, Rust, PHP and plain websites.

It is not for everyone yet, and I want to be clear about that. One project runs on one server. Servers run the latest Ubuntu LTS. An account is for one person for now.

I think software should be as simple as it can be, while still doing all the things you need. ox is my try at that, for putting apps online. The longer version is on the story page, and the features page shows what ox does today.

Key takeaways

  • Hosted tools felt easy but cost a lot; tools on your own server cost less but had busy screens.
  • ox puts a GitHub repo on your own VPS as a systemd service behind Caddy, with no Docker.
  • Every screen shows what you need right now and hides the rest behind Settings.
  • Your AI can write the ox.toml, and ox check catches mistakes before your live site is touched.
  • A failed update leaves the old version serving, and ox says which step failed and how to fix it.

Frequently asked questions

What is ox?

ox is a deploy tool for a server you own. You rent a VPS with the latest Ubuntu LTS, run one command on it once, and pick a GitHub repo. ox builds the app on the server and runs it as a systemd service behind Caddy with HTTPS. It does not use Docker, and every push can go live.

Do I have to host ox myself?

No. Your apps run on your own server, but the ox dashboard is hosted, so you run nothing extra yourself. The server runs only a small agent and Caddy. The agent dials out to ox over an encrypted connection, so no port is opened for it and ox holds no SSH key to your server.

Do I have to write ox.toml by hand?

No. Most apps need no ox.toml, because ox reads your repo and shows what it will run. When you need one, copy the ox guide for your AI into your coding agent, and it writes the file from your code. ox only reads the file when you deploy, so the result is the same every time.

What happens when a deploy fails?

Your live app keeps running. The new version starts next to the old one, and traffic moves only after it passes its health check. If a step fails, your visitors stay on the old version. ox tells you which step failed, why it failed and how to fix it, so you can push again.

Which languages does ox support?

JavaScript and TypeScript, Python, Go, Rust, PHP and plain websites. There are limits to know before you try it: one project runs on one server, servers run the latest Ubuntu LTS, and an account is for one person for now. Each stack has a guide in the docs with a complete ox.toml.

Try it

ox is free while it is in beta. You can sign up with your GitHub account at deploywithox.com. The quickstart takes you from a fresh server to a live app. To see the log buttons I talked about, read the logs page.

Keep reading

Deploy to a server you own

ox sets up systemd, Caddy and PostgreSQL on your Ubuntu server and deploys on every push. Free during the beta.

Sign up with GitHub Read the docs