Back to the blog
BunSelf-hostingDevOpsOpen Source

Self-hosting with Bun: what is worth running on your own server

October 17, 2026·7 min read·Diego Horvatti

Every time I open my credit card bill, there's another 5 dollar subscription I didn't even remember I had. That's why the post What I self-host, from viceconsole.fyi, caught my attention. The author shows what runs on their own infrastructure and explains the reason behind each choice. I read it and went straight to review my own list. This article is my take on the topic, with a specific angle: self-hosting with Bun, which today is the shortest path I know from "I'll spin up a little service" to "it's running".

Why self-hosting is back in the conversation

For about ten years the default answer was "just throw it on a SaaS". It made sense. Servers were a hassle, and developer time is expensive.

Three things changed:

  • VPSs got cheap. For about 5 dollars a month you get 2 vCPUs and 4 GB of RAM. That's enough for a dozen small services.
  • SaaS got expensive. Lots of tools that used to be free became "free tier with 1 project", and everything else is paid now.
  • The runtime got simple. This is the part I care about. Running JavaScript on a server used to require Node, a package manager, a bundler, sometimes a transpiler and a process manager. With Bun, most of that comes in a single executable.

The original post takes a broader path. It covers ready-made services, hardware, the joy of owning your own data. I agree with the spirit. But as a developer, most of what I need to host isn't other people's software. It's my own stuff: bots, webhooks, internal dashboards, scripts that need to run every hour.

What makes sense to host yourself

Before getting into code, it's worth sorting things out. Not everything deserves a spot on your server.

What I host without fear:

  • Internal tools. A metrics dashboard for the blog, a link shortener, a form that sends a notification to Telegram.
  • Webhooks and automations. Things that receive a POST from GitHub or Stripe and do some task.
  • Scheduled jobs. Backups, light scraping, a weekly email report.
  • Prototypes. That idea that might become a project or die in two weeks.

What I don't host:

  • Email. Deliverability is a bottomless pit. Leave it to people who live for that.
  • Auth for a product with paying users. It can be done, but the cost of getting it wrong is too high.
  • Customer databases without a tested backup. If you've never restored a backup, you don't have a backup. You have a file.

Self-hosting is great until you become your own on-call.

That line sums up the rule I use: if it goes down at 3 AM and I only find out at lunch, is that fine? Then it can live at home. If not, pay for the SaaS and sleep.

How lean a Bun server gets

The reason I like Bun so much for this is that it already ships almost everything a small service needs. HTTP server, SQLite, password hashing, file reading, an S3 client and a Postgres client all come in the box.

A complete link shortener, with persistence, fits in this:

import { Database } from "bun:sqlite";

const db = new Database("links.db", { create: true });
db.run("CREATE TABLE IF NOT EXISTS links (slug TEXT PRIMARY KEY, url TEXT NOT NULL)");

const find = db.query<{ url: string }, [string]>("SELECT url FROM links WHERE slug = ?");

Bun.serve({
  port: 3000,
  routes: {
    "/:slug": (req) => {
      const row = find.get(req.params.slug);
      return row ? Response.redirect(row.url, 302) : new Response("Not found", { status: 404 });
    },
  },
});

Zero dependencies. No express, no better-sqlite3 compiling a native binding on the VPS, no dotenv (Bun reads .env on its own). The package.json for this project is basically just the name.

That sounds like a detail, but it isn't. Every dependency is something that can break on an npm install eight months from now, when you don't even remember how the project works. In self-hosting, the enemy isn't performance. It's forgetting.

Single binary: the trick that changes deployment

The part that really changed my workflow is bun build --compile. It bundles your code and the runtime into a single executable.

bun build ./src/index.ts --compile --minify --target=bun-linux-x64 --outfile encurtador
scp encurtador vps:/opt/encurtador/

Done. The VPS doesn't need Bun, Node or anything else installed. The file ends up a few dozen megabytes, because it carries the runtime with it. In exchange you get a deploy that is literally copying a file.

To keep it running, I use what Linux already has. No PM2:

# /etc/systemd/system/encurtador.service
[Service]
ExecStart=/opt/encurtador/encurtador
WorkingDirectory=/opt/encurtador
Restart=always
User=apps

[Install]
WantedBy=multi-user.target

One systemctl enable --now encurtador and the service starts on boot and restarts if it crashes. In front of it I put Caddy, which handles HTTPS by itself with two lines of config per domain.

Docker? I use it when the service is third-party software, like Postgres or an open source tool that already ships with an image. For my own Bun code, binary + systemd is lighter and has fewer moving parts. Strong house opinion: a container for a 40-line script is infrastructure cosplay. If your docker-compose is bigger than your code, something went wrong.

What about the database?

For almost anything personal, SQLite does the job. Seriously. One file, no separate process, no password, no open port. bun:sqlite is fast enough for a volume a personal project will never reach.

When I need Postgres (because the project might grow, or because I want proper jsonb and full text search), Bun has a native client too:

import { sql } from "bun";

const posts = await sql`SELECT slug, title FROM posts WHERE published = true LIMIT 10`;

It reads DATABASE_URL from the environment and uses template literals with escaped parameters. In many projects you can swap out the pg driver without touching much.

Backups are the part nobody likes and everybody should do. Mine is a Bun job that runs overnight, generates the dump and sends it to an S3-compatible bucket using the built-in Bun.s3. Once a month I download one of those files and restore it on a local machine. It takes ten minutes and has already saved me once.

How much it costs and how much work it is

Numbers from my own setup, so you have a reference:

  • VPS: about 5 dollars a month, running 9 services.
  • RAM usage: each idle Bun service sits around 30 to 50 MB. Postgres is the hungriest.
  • Maintenance time: about 30 minutes a month, mostly updating the system and checking backups.

Compared to what I'd pay for equivalent SaaS (link shortener, simple analytics, managed cron, a small database), the bill easily goes past 40 dollars. But saving money isn't the main argument. The argument is that I understand every piece. When something breaks, I know where to look.

There is a downside, and it needs to be said. Security updates are on you. If your VPS gets hacked because you left SSH open with a password, that's on you and nobody else. Set up SSH keys, disable password login, turn on the firewall and enable automatic security updates. It takes twenty minutes the first time and saves a lot of headaches.

Is it worth it for you?

If you're a developer with half a dozen small projects scattered across free tiers that keep changing their rules, yes. Start small. Pick a single service, rewrite it in Bun with no dependencies, compile it, and put it on a VPS with systemd and Caddy. If it hasn't given you any trouble after a month, move the next one.

The viceconsole.fyi post reminded me that owning your infrastructure is more about habit than talent. Bun just made that habit a lot cheaper to keep. Today most of what I run in personal production is one executable file and one .service. If you want to see some of the projects that started this way, they're here in the portfolio.

LinkedIn summary

Every credit card bill brings one more 5 dollar subscription I didn't even remember I had.

Today a 5 dollar VPS runs 9 of my services, and maintenance takes no more than 30 minutes a month.

The secret is Bun: HTTP server, SQLite and an S3 client come in the box, with zero dependencies.

One `bun build --compile` produces a single binary. Deploying became copying a file, with systemd and Caddy in front.

My rule: if it goes down at 3 AM and I only find out at lunch, it can go on my server. If it can't, I pay for the SaaS and sleep.

I wrote on the blog about what is worth hosting yourself and what I leave out. The link is in the comments.

#Bun #SelfHosting #DevOps #JavaScript #Programming