I'm using a Ubuntu 24.04 VM, I started by installing Caddy and Docker as per their websites instructions https://caddyserver.com/docs/install#debian-ubuntu-raspbian https://docs.docker.com/engine/install/ubuntu/ Then I decided what ports I wanted my service running: 8210 = Convex websocket and deploy 8211 = Http actions, which is important for JWT discovery and validation and it NEEDS to be in HTTPS 12791 = Convex Dashboard So I made a folder to contain my docker-compose file and added a .env like so: ``` CONVEX_CLOUD_ORIGIN='https://your.domain.com:8210' CONVEX_SITE_ORIGIN='https://your.domain.com:8211' NEXT_PUBLIC_DEPLOYMENT_URL='https://your.domain.com:8210' ``` And then I downloaded the docker-compose.yml like the Convex Self Host Instructions say https://github.com/get-convex/convex-backend/blob/main/self-hosted/README.md `wget https://github.com/get-convex/convex-backend/raw/refs/heads/main/self-hosted/docker/docker-compose.yml` I made some changes to the docker-compose.yml, like so: For the convex backend I added this 127.0.0.1 so that the container wont open this door publicly, only localhost access ``` ports: - "127.0.0.1:${PORT:-3210}:3210" - "127.0.0.1:${SITE_PROXY_PORT:-3211}:3211" ``` And same for the dashboard: ``` ports: - "127.0.0.1:${DASHBOARD_PORT:-6791}:6791" ``` this is where caddy comes in, I needed these services in HTTPS so I made the vms expose them localhost only so that I can use a reverse proxy in front of them and I did this with Caddy /etc/caddy/CaddyFile ``` { auto_https off } your.domain.com:8080 { tls /path/to/certs/fullchain1.pem /path/to/certs/privkey1.pem reverse_proxy localhost:3000 } your.domain.com:8210 { tls /path/to/certs/fullchain1.pem /path/to/certs/privkey1.pem reverse_proxy localhost:3210 } your.domain.com:8211 { tls /path/to/certs/fullchain1.pem /path/to/certs/privkey1.pem reverse_proxy localhost:3211 } your.domain.com:12791 { tls /path/to/certs/fullchain1.pem /path/to/certs/privkey1.pem reverse_proxy localhost:6791 } ``` I also routed port 3000 > https 8080, which is for my frontend app With this I ran `sudo docker compose up -d` to start the containers With the containers running you can generate an admin key like instructed in the docs: `docker compose exec backend ./generate_admin_key.sh` Then I went to my project and created a .env.deploy with my server configs: ``` CONVEX_SELF_HOSTED_URL='https://your.domain.com:8210' CONVEX_SITE_ORIGIN='https://your.domain.com:8211' CONVEX_SELF_HOSTED_ADMIN_KEY='' ``` Before deploying, check that `auth.config.ts` has the correct environment variable set: ``` export default { providers: [ { domain: process.env.CONVEX_SITE_ORIGIN, applicationID: "convex", }, ], }; ``` And at this point I could just deploy with `npx convex deploy --env-file .env.deploy` (I did this so that I can still have my localhost to safely cause havok in) And also very important make sure the convex backend has this variable set, this does not come from these files, this is specifically from the Environments tab on convex dashboard: `npx convex env set CONVEX_SITE_ORIGIN https://your.domain.com:8211 --env-file .env.deploy` And next step is to create the keys for JWT, I used this `generatekeys.mjs` honestly I don't remember where I got it from, but it was in the docs: ``` import { exportJWK, exportPKCS8, generateKeyPair } from "jose"; const keys = await generateKeyPair("RS256", { extractable: true, }); const privateKey = await exportPKCS8(keys.privateKey); const publicKey = await exportJWK(keys.publicKey); const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] }); process.stdout.write( `JWT_PRIVATE_KEY="${privateKey.trimEnd().replace(/\n/g, " ")}"`, ); process.stdout.write("\n"); process.stdout.write(`JWKS=${jwks}`); process.stdout.write("\n"); ``` this will print 2 variables we need set in the convex backend, like the one we set earlier, and in the same fashion you can set them from the command line or directly from the dashboard From the commandline `npx convex env set JWT_PRIVATE_KEY 'the key' --env-file .env.deploy` `npx convex env set JWKS 'the json with jwks' --env-file .env.deploy` I couldn't get it to work from the command line for these 2, maybe my machine or skill issue, but I set them from the dashboard and it worked just right With all that set you can deploy with `npx convex deploy --env-file .env.deploy`, deploy your frontend (make sure it's calling the correct convex urls) and see if it works In my case it didn't, and I ran into 2 problems: - Caddy wasn't being able to read my certificates, I made a copy of them on a folder with group cady and permission 640 and all set - The container was not being able to reach the host machine through the "CONVEX_SITE_ORIGIN"; Idk what happened there, the container would reach google and anywhere internet, but not my container, so I added this gimmick to the compose file: ``` extra_hosts: - "your.domain.com:172.17.0.1" ``` compose down, compose up and it was all set