Deploy a Bolt.new app to your own server
How to deploy a Bolt.new app outside Bolt Cloud: get the code, find out which stack Bolt chose, build it in Docker, serve it over HTTPS, and claim the database into Supabase.
TL;DR
- Connect the project to GitHub (opens in a new tab) from its GitHub icon (Bolt (opens in a new tab) creates a private repository on
mainand commits after each change), or download it as a zip from the project title menu. - Bolt builds only JavaScript apps;
package.jsonshows whether you have a static Vite build that needs anindex.htmlfallback or a Node.js (opens in a new tab) server that needs Node running behind a reverse proxy. - Values in
.envthat the browser reads are compiled in at build time, while Bolt's secrets live only on Bolt's servers and are not in the code. - To own the data, claim Bolt Database into your Supabase (opens in a new tab) organisation (Pro or Teams plan), then point the app at that project.
On this page
To deploy a Bolt.new app to your own server, connect the project to GitHub (or download it), check which framework Bolt chose in package.json, build it into a Docker (opens in a new tab) image with every browser-side variable set at build time, and run it behind a reverse proxy that serves your domain over HTTPS. This article is part of the guide to deploying an AI-built app to your own server.
The Bolt facts here come from Bolt's help centre, checked on 11 November 2026. The Dockerfile and the Caddy (opens in a new tab) and Compose files were built and run that day in Docker on Node.js 22, against a fresh Vite and React app from npm create vite@latest (Vite 8).
What stays on Bolt, and what can move
Bolt runs more of your app than the code suggests:
- Hosting. Bolt Cloud publishes the app at a free
.bolt.hostaddress, with custom domains on the Pro plan, or to Netlify through Bolt's integration. - Bolt Database. The built-in database Bolt creates when the app needs one or you ask for one, with sign-in (email and Google), file storage buckets, server functions, secrets, logs and a security audit.
- Server functions and secrets. Bolt's secrets are API keys and passwords for outside services, such as a payment or email provider. They work only in server functions, which run on Bolt's servers, and Bolt never writes their values into your files, your browser code or the published site.
The code moves with GitHub or a download; the rest does not move by itself. Bolt's docs do not describe using Bolt Database or its server functions from an app hosted outside Bolt, so plan for two moves: the frontend to your server, and the backend to a Supabase project you own (step 4).
Step 1: Get the code out of Bolt
GitHub (keeps syncing). You need an individual GitHub account; organisation repositories need Bolt's separate organisation setup.
- Open the project and click the GitHub icon at the top right. Log in, authorise Bolt and install its GitHub app, for all repositories or only the ones you pick.
- Name the repository and click Create repository. It starts private, on the
mainbranch.
From then on, Bolt commits after each change that does not break the project, and checks GitHub every 30 seconds for commits made elsewhere. Bolt stays on main; you can create other branches in GitHub, but the Bolt project cannot switch to them. Only the project owner manages the connection, and disconnecting is permanent unless Bolt's support reconnects it, so leave it connected while you still build in Bolt.
Download (a one-time copy). In a desktop browser, click the project title at the top left, then Download.
Either way, run the app on your own computer first (npm install && npm run dev): it is the fastest way to see every variable it needs.
Step 2: Find out which stack Bolt chose
Bolt builds only JavaScript apps, with Node.js for any backend; package.json shows which framework it chose:
vitewithreact(or Vue, Svelte) and no server framework: a static single-page app. The build writes plain files todist/, which any web server can serve if it answers unknown paths withindex.html, as steps 5 and 6 do.next: a Next.js (opens in a new tab) app, which runs as a Node.js server. The v0 guide has a tested standalone Next.js Dockerfile.express,fastify,honoor astartscript that runsnode: a Node.js server. The Replit guide has a tested Dockerfile for that case, with PostgreSQL (opens in a new tab) beside it.
Step 3: List the environment variables and secrets
Bolt splits configuration in two:
- Environment variables live in the project's
.envfile. Values the browser reads end up in what visitors download: with Vite, the variables whose names start withVITE_, which Vite replaces in the code at build time. So they must be present when you build, and must never hold anything private. When the project has a database, Bolt fills some of these in itself and locks them. - Secrets (open the Database icon, then Secrets) are not in
.envor anywhere in the code, and Bolt never shows a saved value again. Write down their names now, and get each value again from the service it belongs to (your Stripe or Resend dashboard, for example); you will need it wherever the server functions end up.
Step 4: Take ownership of the database
Bolt offers two ways to use Supabase, and only one of them keeps your data:
- Claim Bolt Database into Supabase. On the Pro or Teams plan, connect Bolt to Supabase under Account settings, then Applications. Open the project's Database icon, go to Advanced and click Claim, then finish the steps in Supabase. You must be an owner of the Supabase organisation. The database then appears as a project in your Supabase account. Claiming into a free Supabase plan turns off leaked password protection, which needs Supabase Pro.
- Connect a different Supabase database. This replaces the project's database connection, and Bolt warns that it may cause data loss when the project already has a Bolt database. Use it only for a project that has none.
Bolt's docs do not list exactly what a claim carries over, so check the new Supabase project before you rely on it: the tables and their rows, the users under Authentication, the storage buckets and the functions. Then:
- Point the app's browser-side variables at the claimed project (its URL and publishable key, from the Supabase dashboard).
- Check where the app's server functions run after the claim, and set the secrets from step 3 there.
- Add your new domain to Supabase's URL Configuration page: the Site URL, which is the default redirect address that confirmation and password-reset emails use, and the Redirect URLs list. Google sign-in also needs the new address in its OAuth settings.
Step 5: The Dockerfile for a static Bolt app
The image builds the files with Node.js 22 and serves only dist/ with Caddy on port 8080. List every browser-side variable from step 3 as a build argument; the names below are examples for a Supabase-backed app:
# Build stage: Node 22 builds the static files into dist/
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# Every variable the browser reads, set at build time
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_ANON_KEY
RUN npm run build
# Run stage: Caddy serves dist/ and sends unknown paths to index.html
FROM caddy:2-alpine
COPY spa.Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /srvnpm ci needs a package-lock.json; if the repository has none, run npm install once and commit the lock file it writes. The spa.Caddyfile answers any path that is not a file with index.html, so routes such as /dashboard work when a visitor refreshes or opens a link:
:8080 {
root * /srv
encode
try_files {path} /index.html
file_server
}And a .dockerignore, so the local .env, node_modules and Git history stay out of the build:
node_modules
dist
.git
.env
.env.*Step 6: Run it behind Caddy with HTTPS
A second Caddy, in front, takes ports 80 and 443 and passes requests to the app. The Caddyfile:
app.example.com {
reverse_proxy app:8080
}And compose.yaml, which passes the build arguments and keeps Caddy's certificates in a volume:
services:
app:
build:
context: .
args:
VITE_SUPABASE_URL: ${VITE_SUPABASE_URL}
VITE_SUPABASE_ANON_KEY: ${VITE_SUPABASE_ANON_KEY}
restart: unless-stopped
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:Replace app.example.com with your domain and put the values in a .env file next to compose.yaml, which Docker Compose reads for the ${...} substitutions. Point the domain's A record at the server, then:
docker compose up -d --buildCaddy gets and renews the certificate once the domain resolves to the server and ports 80 and 443 are open, and redirects HTTP to HTTPS. In our test, with localhost as the site address (Caddy then uses its own local certificate authority, not a public one), the app answered over HTTPS, a deep link such as /dashboard/settings returned the app with status 200, and the Supabase URL passed as a build argument was in the built JavaScript. A public certificate for a real domain needs a real server, which the test did not use.
No server yet? How to deploy a web app to a VPS covers creating one, SSH keys, the firewall and Docker, and Hetzner vs DigitalOcean compares two good places to rent it.
Step 7: Test, then switch the domain
On a temporary address, such as a test subdomain pointed at the server:
- Open a deep link and refresh it. A 404 means the
index.htmlfallback is missing. - Sign up, confirm the email, sign in and out. A redirect error means the address is not in Supabase's Redirect URLs.
- Read and write data, and upload a file to each bucket the app uses.
- Trigger each server function, such as a payment or an email.
- Search the code for
.bolt.host, in links, callbacks and webhook settings.
Then point the domain at the server, and if it was connected to Bolt Cloud, remove it there once the new server answers. For a second worked example, the Lovable guide covers a very similar app with its own Supabase-based backend.
Doing this with OpsCrew
OpsCrew recognises a repository exported from Bolt and starts with a free opscrew scan for leaked keys and exposed Supabase data, such as a table without Row Level Security. It then builds the image in GitHub Actions (opens in a new tab) with its own Dockerfile for Vite apps and Node.js servers such as Express (or with yours), handles browser-side variables as public build-time values, and deploys to a new server on Hetzner Cloud (opens in a new tab) or DigitalOcean (opens in a new tab) or to a server you already have, with HTTPS on your domain. A static Bolt app can instead go on Cloudflare's hosting, with no server. Each change waits for your approval of its exact plan. Claiming the database into Supabase stays a step you take in Bolt.
For what is the same for every builder, see the guide to deploying an AI-built app to your own server.
Key facts
| Fact | Value | Source |
|---|---|---|
| Languages Bolt builds with | JavaScript only; Node.js for backends, any browser JavaScript framework for frontends | Supported technologies (opens in a new tab)Verified on |
| How often Bolt pulls changes from GitHub | Every 30 seconds | GitHub for version control (opens in a new tab)Verified on |
| Plans that can claim Bolt Database into Supabase | Pro and Teams | Supabase for databases (opens in a new tab)Verified on |
| Where Bolt's secrets can be used | Only in server functions, which run on Bolt's servers | Secrets (opens in a new tab)Verified on |
Frequently asked questions
Can I deploy a Bolt.new app to the App Store?
Only if it was built as a mobile app with Bolt's Expo integration. Bolt's docs publish those through Expo Application Services (EAS) with an Expo account and an Apple Developer or Google Play Developer account. A web app built in Bolt does not convert easily into a mobile one; this guide covers web apps.
Why is my Bolt app blank or missing data after I deploy it myself?
Usually a browser-side variable from .env was not set when the app was built, so the build has an empty value. Pass every variable the browser reads (for a Vite app, the VITE_ ones) as a build argument and build again.
Sources
- GitHub for version control (opens in a new tab)
- Manage your projects (opens in a new tab)
- Supported technologies (opens in a new tab)
- Glossary (opens in a new tab)
- Host your Bolt project (opens in a new tab)
- Bolt Database (opens in a new tab)
- Advanced database settings (opens in a new tab)
- Secrets (opens in a new tab)
- Supabase for databases (opens in a new tab)
- Expo for mobile apps (opens in a new tab)
- Env variables and modes (opens in a new tab)
- Redirect URLs (opens in a new tab)
- Automatic HTTPS (opens in a new tab)
Related articles
- Deploying
How to deploy an app you built with AI to your own server
From a Lovable, Bolt.new, Replit, v0 or Claude Code project to a live URL on a server you control: code, build, backend, secrets, HTTPS and what breaks on the way.
- Deploying
Deploy a Lovable app to your own server (Vite or TanStack Start)
How to deploy a Lovable app to a server you control: tell the Vite and TanStack Start stacks apart, sync it to GitHub, build it in Docker, serve it over HTTPS, and keep or move the Supabase backend.
- Deploying
Deploy a Replit app to a VPS
How to move a Replit app to a VPS you control: push it to GitHub, turn the .replit build and run commands into a Dockerfile, re-create the secrets, and move the production database with pg_dump.