DEV Community

Jookllo
Jookllo

Posted on

Mastering Self-Hosted Convex: A Complete Deployment Guide

Mastering Self-Hosted Convex: A Complete Deployment Guide

So, you've decided to take control of your data and self-host Convex. Great choice! But if you've tried pushing your functions to a local or private server, you might have run into cryptic errors about "Relative import paths" or "Missing modules."

The secret? You don't need custom deployment scripts. In fact, you shouldn't use them. The official Convex CLI is actually powerful enough to handle self-hosted deployments perfectly—if you know which buttons to press.

In this guide, I’ll show you exactly how to configure your project and deploy your functions like a pro.


1. Before You Start

Before we dive into the code, make sure you have the following ready:

  • Your Convex Instance: Whether you're using Dokploy, Docker, or a raw VPS, your backend should be up and running.
  • The Admin Key: You'll need the master key for your server.
    • Tip: If you're using Dokploy, you can usually generate this by running ./generate_admin_key.sh inside your backend container's terminal.
  • The CLI: Make sure you have the latest version of Convex installed locally:

    npm install convex
    

2. Preparing Your Project

For a smooth deployment, your convex/ folder needs to be a bit more "independent."

Step 1: Give Convex its own Identity

Create a package.json inside your convex/ directory. This ensures the CLI knows exactly which dependencies to bundle when it prepares your code for the server.

{
  "name": "my-convex-backend",
  "private": true,
  "type": "module",
  "dependencies": {
    "convex": "^1.16.0",
    "typescript": "^5.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Fix your Config

Ensure your convex/convex.config.ts is using the modern defineApp export. The server expects this structure to understand your app's layout.

import { defineApp } from "convex/server";

export default defineApp();
Enter fullscreen mode Exit fullscreen mode

3. The Magic Environment Variables

The Convex CLI is "Cloud-first" by default, but we can tell it to target our own server by setting two specific environment variables.

Variable What it does
CONVEX_SELF_HOSTED_URL Points the CLI to your backend URL (e.g., https://api.myapp.com).
CONVEX_SELF_HOSTED_ADMIN_KEY Your secret key. Crucial: It must include the `convex-self-hosted

4. The Big Moment: Deploying

Now, let's push your code. We're going to use the official {% raw %}deploy command.

Run this from your root or convex directory:

CONVEX_SELF_HOSTED_URL="https://your-api-url.com" \
CONVEX_SELF_HOSTED_ADMIN_KEY="convex-self-hosted|your-key-here" \
npx convex deploy --typecheck disable
Enter fullscreen mode Exit fullscreen mode

Why this works where other scripts fail:

When you use a custom fetch script to send files, you're just sending raw text. The server has no idea where convex/server is.

The CLI is smarter. It uses esbuild to "bundle" your code into a single, self-contained JavaScript package. It finds all your imports and bakes them right into the file so the server can run them instantly.


5. Pro Tip: Automate Everything

Don't type those long variables every time. Add a script to your convex/package.json:

"scripts": {
  "deploy": "CONVEX_SELF_HOSTED_URL=... CONVEX_SELF_HOSTED_ADMIN_KEY=... npx convex deploy --typecheck disable"
}
Enter fullscreen mode Exit fullscreen mode

Now, all it takes is a simple:

npm run deploy
Enter fullscreen mode Exit fullscreen mode

Troubleshooting common "Gotchas"

"Relative import path 'convex/server' not found"

If you see this, you likely bypassed the CLI. Always use npx convex deploy so the bundler can do its job!

"BadAdminKey"

Double-check your key. Most people forget the convex-self-hosted| prefix. Without it, the server will reject your request every time.

Missing _generated files in your frontend

Run npx convex codegen while your CONVEX_SELF_HOSTED_URL is set to update your local TypeScript types to match what you just deployed.


Happy hosting! Your Convex backend is now truly yours.

Top comments (0)