Self-hosting
Self-hosted Convex
Use the optional dashboard and migrate CLI to operate your backend.
Open the optional dashboard
Run commands from the installation directory, where Compose reads .env. For source installs, add --env-file .env.docker after docker compose.
docker compose --profile debug up -d dashboardThe dashboard binds to 127.0.0.1:6791; DASHBOARD_PORT changes the host port. On a remote server, use an SSH tunnel to reach it. Its NEXT_PUBLIC_DEPLOYMENT_URL comes from CONVEX_PUBLIC_URL. Log in with CONVEX_SELF_HOSTED_ADMIN_KEY from .env. Dashboard access grants administration of all stored data, so keep it private.
Use the migrate CLI
With no arguments, migrate pushes non-empty environment settings and deploys functions, then exits. With arguments, it passes them directly to the bundled Convex CLI. It uses http://convex:3210 and the admin key from Compose; it does not select a cloud deployment.
docker compose run --rm migrate logs --history 50
docker compose run --rm migrate env listTo set a backend value directly, use docker compose run --rm migrate env set NAME VALUE. Keep values also supplied in .env in sync: the next deployment reapplies those non-empty values. An empty .env value does not erase an existing backend setting. See environment variables.
Export a snapshot
The CLI writes paths inside the migrate container. Compose does not mount a host backup directory by default. Keep a named container until you copy the export out:
docker compose run --name opensend-backup migrate export --include-file-storage --path /tmp/backup.zip
docker cp opensend-backup:/tmp/backup.zip ./backup.zip
docker rm opensend-backupInclude file storage to recover attachments and received mail. A snapshot includes component data. Save .env securely alongside it. Pause writes for the backup; a consistent whole-volume copy also requires stopping Convex.
Restore a snapshot
Restore into a separate installation and volume using the backup's release files, images, and secrets. Choose a different COMPOSE_PROJECT_NAME, ports, and public URLs to isolate it from production. Start its Convex service and deploy the matching functions to create the component tree. Then mount the host directory containing backup.zip read-only for import:
docker compose up -d convex
docker compose run --rm migrate
docker compose run --rm -v "$PWD:/backup:ro" migrate import --replace-all /backup/backup.zip
docker compose up -d --waitThe default import requires empty tables. Here, --replace-all restores the snapshot across the target deployment and clears data absent from the snapshot. The CLI asks for confirmation when deleting existing documents. Use a separate target; do not test a restore against production. Verify users, teams, encrypted settings, and file downloads before switching traffic. See upgrading.
Re-mint the admin key
The admin key is derived from INSTANCE_NAME and INSTANCE_SECRET by the pinned backend image. If the key is lost but those values remain, generate it again without changing them:
CONVEX_IMAGE=$(docker compose config --images convex)
INSTANCE_NAME=$(awk -F= '$1 == "INSTANCE_NAME" { print $2; exit }' .env)
INSTANCE_SECRET=$(awk -F= '$1 == "INSTANCE_SECRET" { print $2; exit }' .env)
docker run --rm --entrypoint ./generate_key "$CONVEX_IMAGE" "$INSTANCE_NAME" "$INSTANCE_SECRET"
unset INSTANCE_SECRETReplace CONVEX_SELF_HOSTED_ADMIN_KEY in .env with the result and keep it secret. These reads match the installer's unquoted generated values. Do not rotate INSTANCE_SECRET to recover a lost key. The installer never rotates existing secrets on rerun.
Develop from source
The product repository also provides pnpm backend, which reads .env.docker. For direct CLI/editor use, set CONVEX_SELF_HOSTED_URL and CONVEX_SELF_HOSTED_ADMIN_KEY in .env.local as .env.example shows. Do not also set CONVEX_DEPLOYMENT. OPENSEND_ENV_FILE overrides the source helper's environment file.