DEV Community

RyanCwynar
RyanCwynar

Posted on • Originally published at ryancwynar.com

Why I Self-Host Convex as My Personal API Layer

Every side project starts the same way: you need a database, an API, and auth. By the third project, you're drowning in Supabase instances and Railway deployments. I got tired of it, so I self-hosted Convex on a single VPS and turned it into the backbone for everything I build.

The Problem with Per-Project Backends

Most indie hackers spin up a new backend for every project. A Postgres here, a serverless function there, maybe a Firebase instance for real-time stuff. It works until you have six projects, four databases, and no idea which one has the customer data you need.

I wanted one place where all my data lives, all my functions run, and I can wire up new projects in minutes instead of hours.

Why Convex

Convex gives you a reactive database, serverless functions, and real-time subscriptions out of the box. The self-hosted version means I own the data and the compute. No vendor lock-in, no surprise bills, no rate limits.

The killer feature for my use case: functions are just TypeScript. I write a query or mutation, deploy it, and it's immediately callable from any project. No REST routes to define, no API gateway to configure. The function is the API.

The Setup

My stack is dead simple:

  • One $10/mo VPS running Docker
  • Traefik for reverse proxy and automatic HTTPS
  • Convex self-hosted (two containers: backend + dashboard)
  • PostgreSQL for Convex's storage layer

Deploy command is one line:

CONVEX_SELF_HOSTED_URL="https://convex-socket.byldr.co" \
CONVEX_SELF_HOSTED_ADMIN_KEY="your-admin-key" \
npx convex deploy
Enter fullscreen mode Exit fullscreen mode

Every project in the repo shares the same Convex instance. New tables, new functions — just deploy and they're live.

What Runs on It

Right now, my single Convex instance powers:

  • Blog engine — posts, cross-posting to Dev.to and Hashnode
  • Contact forms — submissions from multiple sites
  • Prospect tracking — leads, payment links, follow-ups
  • API key management — auth for external integrations
  • Webhook logging — every inbound webhook gets logged
  • Email system — templates, send logs, credential management

That's six distinct "products" sharing one backend. Adding a new one takes maybe 20 minutes: define the schema, write the functions, deploy.

The Real Win: Cross-Project Queries

Here's what you can't do with separate backends: query across projects. When I want to see all activity — blog posts published, prospects contacted, emails sent — it's one query against one database. No API stitching, no data pipeline, no ETL.

My AI agent uses this constantly. It calls Convex functions to publish blog posts, check prospect status, and log activities. One set of credentials, one deployment target, zero context-switching.

What I'd Do Differently

I'd set up proper table namespacing earlier. Right now my tables are a flat list (posts, prospects, emailLogs). With ten more projects, that gets messy. Convex doesn't have schemas-within-schemas, but a naming convention like blog_posts, crm_prospects would help.

I'd also add a read-only dashboard earlier. The Convex dashboard is great for debugging, but I want a simple status page showing counts and recent activity across all projects.

Should You Do This?

If you're building one SaaS product, probably not. Use Convex Cloud or whatever managed service fits.

But if you're an indie hacker with multiple projects, a self-hosted Convex instance is genuinely underrated. You get a unified data layer, real-time by default, and TypeScript functions that deploy in seconds. All on a cheap VPS you already have.

The best infrastructure is the kind you set up once and forget about. Six months in, I've deployed dozens of function updates and haven't touched the Docker config once. That's the goal.

Top comments (0)