Skip to main content
RapidDev - Software Development Agency
lovable-integrationsDeveloper Workflow

How to Integrate Lovable with Docker

Lovable generates standard Vite+React+TypeScript projects with no proprietary dependencies, making them fully containerizable with Docker. Export your project via GitHub, write a multi-stage Dockerfile using node:22 for the build and nginx for static serving, and deploy the container to AWS ECS, Google Cloud Run, or any Docker-compatible host. Docker gives you reproducible builds and infrastructure-agnostic deployments.

What you'll learn

  • How to export a Lovable project from GitHub and prepare it for Docker
  • How to write a multi-stage Dockerfile for a Vite+React static site
  • How to use docker-compose for local development with hot reload
  • How to deploy the container to AWS ECS, Google Cloud Run, or a self-hosted server
  • How to handle environment variables in Docker for Supabase connections
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner14 min read30 minutesDevOpsMarch 2026RapidDev Engineering Team
TL;DR

Lovable generates standard Vite+React+TypeScript projects with no proprietary dependencies, making them fully containerizable with Docker. Export your project via GitHub, write a multi-stage Dockerfile using node:22 for the build and nginx for static serving, and deploy the container to AWS ECS, Google Cloud Run, or any Docker-compatible host. Docker gives you reproducible builds and infrastructure-agnostic deployments.

Containerizing a Lovable Project with Docker for Flexible Deployment

Lovable deploys projects to its own Lovable Cloud hosting by default, which is the easiest option for most use cases. But there are legitimate reasons to containerize and self-host: compliance requirements that mandate data residency in specific regions, cost optimization at scale, integration with existing Kubernetes clusters, or enterprise policies that prohibit third-party hosting. Because Lovable generates a completely standard Vite+React+TypeScript project with no proprietary runtime dependencies, containerizing it with Docker is straightforward.

The build process has two stages. First, a node:22 container installs dependencies and runs npm run build, producing a static dist/ folder containing your compiled HTML, JavaScript, and CSS. Second, an nginx container serves that static output. The final image is remarkably small — typically under 50 MB — because it only contains nginx and the compiled static files, not Node.js itself. This two-stage approach is a Docker best practice called a multi-stage build, and it is the standard pattern for React app containerization.

For local development, docker-compose adds value by mounting your source code as a volume and running the Vite dev server inside a container, giving you hot module replacement without installing Node.js locally. For production, the same Dockerfile works on AWS ECS, Google Cloud Run, DigitalOcean App Platform, or any server with Docker installed. The key consideration is environment variables: Supabase credentials are build-time variables in Vite (prefixed with VITE_), so they need to be passed to docker build as build arguments, not as runtime environment variables injected into the running container.

Integration method

Developer Workflow

Lovable exports projects as standard Vite+React+TypeScript code via GitHub. You containerize this output with a Dockerfile: a node:22 build stage runs npm run build to produce a dist/ folder, and an nginx stage serves it as a static site. The resulting container image can be deployed anywhere Docker runs — local machines, AWS ECS, Google Cloud Run, DigitalOcean, or self-hosted servers.

Prerequisites

  • A Lovable project connected to a GitHub repository (see the VS Code integration guide for GitHub setup steps)
  • Docker Desktop installed on your computer — download from docker.com/products/docker-desktop
  • A GitHub account to clone your Lovable project repository
  • Basic familiarity with opening a terminal and running commands
  • For cloud deployment: an account with AWS, Google Cloud, or DigitalOcean (free tiers available on all three)

Step-by-step guide

1

Connect Lovable to GitHub and clone the project

Docker works with the code files that Lovable generates, so you first need to export your project from Lovable to GitHub. Open your Lovable project in the browser. Click the GitHub icon in the top-right corner. If your project is not already connected, follow the OAuth flow to authorize Lovable with your GitHub account, then click 'Connect project' and choose to create a new repository or link an existing one. Once connected, the GitHub icon turns green, indicating real-time sync is active. Now open your terminal and clone the repository using the HTTPS URL from GitHub: navigate to the repository page, click the green 'Code' button, copy the URL, and run git clone followed by the URL. Once cloned, open the folder in your terminal. You will see the standard Lovable project structure: src/ for React components, supabase/ for Edge Functions, vite.config.ts, tailwind.config.ts, tsconfig.json, and package.json at the root. Confirm that the project builds cleanly before dockerizing: run npm install followed by npm run build and verify that a dist/ folder appears containing index.html and the compiled JavaScript and CSS assets. If the build succeeds locally, it will succeed inside Docker. If it fails, fix the build errors first — they will cause the Docker build to fail at the same step.

Pro tip: Run npm run build locally before writing your Dockerfile. A successful local build confirms the project structure is correct and helps you spot environment variable issues before they become Docker problems.

Expected result: You have a local clone of your Lovable project. Running npm run build produces a dist/ folder without errors. You are ready to write the Dockerfile.

2

Write a multi-stage Dockerfile for the Vite+React project

Create a file named Dockerfile in the root of your project — the same directory as package.json. The Dockerfile uses a two-stage build pattern: the first stage installs dependencies and compiles the TypeScript/React code into static files, and the second stage copies only those static files into a minimal nginx image. This keeps the final Docker image small by excluding Node.js and all the node_modules from the production image. The build stage uses node:22-alpine (the Alpine Linux variant is much smaller than the default Debian image). It sets the working directory, copies package.json and package-lock.json first (so Docker caches the npm install layer and only re-runs it when dependencies change), installs dependencies with npm ci (cleaner and faster than npm install for CI/CD), then copies the rest of the source code and runs npm run build. The VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are declared as ARG build arguments — they must be passed at build time because Vite embeds these values into the compiled JavaScript bundle at build time, not at runtime. The nginx stage starts from nginx:alpine, copies the dist/ folder output from the build stage into nginx's web root, and copies a custom nginx.conf that handles single-page application routing (redirecting all paths back to index.html so React Router works correctly). The final image serves the app on port 80.

Dockerfile
1# Stage 1: Build
2FROM node:22-alpine AS builder
3
4WORKDIR /app
5
6# Copy package files first for better layer caching
7COPY package.json package-lock.json ./
8RUN npm ci
9
10# Build args for Vite environment variables
11# These are embedded into the bundle at build time
12ARG VITE_SUPABASE_URL
13ARG VITE_SUPABASE_ANON_KEY
14ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
15ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY
16
17# Copy source and build
18COPY . .
19RUN npm run build
20
21# Stage 2: Serve with nginx
22FROM nginx:alpine
23
24# Copy built files
25COPY --from=builder /app/dist /usr/share/nginx/html
26
27# Copy nginx config for SPA routing
28COPY nginx.conf /etc/nginx/conf.d/default.conf
29
30EXPOSE 80
31
32CMD ["nginx", "-g", "daemon off;"]

Pro tip: The ARG declarations must come after the FROM line for the stage where they are used. Build args declared before the first FROM are only available to FROM instructions themselves (for dynamic base image selection).

Expected result: A Dockerfile exists in your project root. The file has two FROM stages: node:22-alpine for building and nginx:alpine for serving.

3

Create the nginx configuration for SPA routing

React Router uses the HTML5 History API to handle navigation — URLs like /dashboard or /settings are handled client-side by React, not by the server. Without a special nginx configuration, requesting /dashboard directly (or refreshing the page on that route) would return a 404 error because there is no actual file at that path — only the root index.html exists. You need to configure nginx to serve index.html for any path that does not match an existing file. Create a file called nginx.conf in your project root. The configuration defines a server block listening on port 80, sets the root directory to /usr/share/nginx/html where the Vite build output lives, and uses the try_files directive to attempt serving the exact file path first, then fall back to index.html if no file is found. This is the standard SPA routing configuration and is required for any React Router application served statically. The configuration also sets the index file to index.html and enables gzip compression for better performance. The location block for static assets (JavaScript, CSS, images) sets aggressive cache headers since Vite generates content-hashed filenames — the same file content always has the same hash in the filename, so it is safe to cache indefinitely. The main location block sets no-cache headers for index.html itself, ensuring users always get the latest version of the app.

nginx.conf
1server {
2 listen 80;
3 server_name _;
4 root /usr/share/nginx/html;
5 index index.html;
6
7 # Enable gzip compression
8 gzip on;
9 gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
10
11 # Cache static assets with content hashes forever
12 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
13 expires 1y;
14 add_header Cache-Control "public, immutable";
15 }
16
17 # Never cache index.html
18 location = /index.html {
19 add_header Cache-Control "no-cache, no-store, must-revalidate";
20 }
21
22 # SPA routing: serve index.html for all other paths
23 location / {
24 try_files $uri $uri/ /index.html;
25 }
26}

Pro tip: If your Lovable app is deployed at a subdirectory path (e.g., example.com/app/), you need to set the base path in vite.config.ts using the base option and adjust the nginx location block accordingly.

Expected result: An nginx.conf file exists in your project root. React Router navigation will work correctly when the container is running — direct URL access to /dashboard and other routes will load the React app, not return 404.

4

Build and test the Docker image locally

With the Dockerfile and nginx.conf in place, you can now build and test the Docker image on your local machine before deploying to the cloud. Open your terminal in the project root. Build the Docker image by running the docker build command. You need to pass your Supabase credentials as build arguments using the --build-arg flag because Vite embeds them into the compiled JavaScript at build time. Replace the placeholder values with your actual Supabase project URL and anon key — you can find these in Lovable's Cloud tab by clicking the + icon next to Preview, or in your Supabase project settings under Project Settings → API. Give the image a name using the -t flag. The build process runs both stages: the builder stage installs dependencies (this takes one to three minutes the first time) and compiles the TypeScript code, then the nginx stage creates a minimal image with only the compiled output. Once the build succeeds, run the container locally using docker run with port mapping to expose port 80 inside the container as port 8080 on your machine. Open http://localhost:8080 in your browser to verify the app loads and navigation works. Test a direct URL like http://localhost:8080/some-page to confirm the nginx SPA routing is working correctly — you should see the React app, not a 404 page.

typescript
1# Build the image (replace with your actual Supabase values)
2docker build \
3 --build-arg VITE_SUPABASE_URL=https://your-project-id.supabase.co \
4 --build-arg VITE_SUPABASE_ANON_KEY=your-anon-key-here \
5 -t my-lovable-app:latest .
6
7# Run the container locally
8docker run -p 8080:80 my-lovable-app:latest
9
10# Or run in background
11docker run -d -p 8080:80 --name lovable-app my-lovable-app:latest

Pro tip: Add a .dockerignore file to your project root listing node_modules, .git, .env.local, and dist. This prevents Docker from copying these folders into the build context, making builds faster.

Expected result: The docker build command completes without errors and produces an image. Running the container and opening http://localhost:8080 shows your Lovable app. All routes work correctly. The Docker image size should be between 20 MB and 60 MB.

5

Set up docker-compose for local development with hot reload

The production Dockerfile builds a static image, but for local development you want hot module replacement — changes to React components should appear in the browser immediately without rebuilding the entire image. Docker Compose solves this by mounting your source code as a volume into a Node.js container running the Vite dev server. Create a docker-compose.yml file in your project root. The configuration defines a service using the node:22-alpine image, mounts the entire project directory as a volume, overrides the default command to run npm run dev, sets the VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY environment variables using your .env.local file, and maps port 5173 from the container to port 5173 on your host machine. Vite's dev server is configured by default to listen on port 5173. After creating this file, create the .env.local file if it does not already exist and add your Supabase credentials. Run docker compose up to start the development server. Docker will pull the node:22-alpine image, install dependencies inside the container, and start the Vite dev server. Open http://localhost:5173 in your browser to see the app running. Changes you make to files in the src/ directory will trigger instant hot module replacement in the browser, just as if you had run npm run dev directly on your machine — except everything runs inside Docker.

docker-compose.yml
1version: '3.8'
2
3services:
4 app:
5 image: node:22-alpine
6 working_dir: /app
7 volumes:
8 - .:/app
9 - /app/node_modules
10 command: sh -c "npm install && npm run dev -- --host"
11 ports:
12 - "5173:5173"
13 env_file:
14 - .env.local
15 environment:
16 - NODE_ENV=development

Pro tip: The -- --host flag passes --host to the Vite dev server, making it listen on all network interfaces inside the container (0.0.0.0) rather than only localhost. This is required for port mapping to work correctly from container to host.

Expected result: Running docker compose up starts the Vite dev server inside a container. Opening http://localhost:5173 shows the app. Editing a React component in VS Code triggers an instant update in the browser through hot module replacement.

Common use cases

Deploy a Lovable app to a self-hosted VPS or internal server

Teams with on-premise infrastructure or compliance requirements can containerize their Lovable project and run it on any Linux server with Docker installed. Build the image locally, push it to a container registry, and pull and run it on the server — no Lovable Cloud subscription required for hosting.

Lovable Prompt

I want to self-host my Lovable app using Docker. Can you generate a production-ready Dockerfile for a Vite React TypeScript project that uses nginx to serve static files?

Copy this prompt to try it in Lovable

Run the Lovable project locally without installing Node.js

Developers who do not want to install Node.js globally can use docker-compose to run the Vite development server inside a container. Source code is mounted as a volume so changes on the host machine trigger hot reload inside the container.

Lovable Prompt

Create a docker-compose.yml file for local development of my Lovable Vite React project with hot reload enabled.

Copy this prompt to try it in Lovable

Deploy to Google Cloud Run for serverless container hosting

Google Cloud Run scales containers from zero to many instances based on traffic and charges only for actual request processing time, making it cost-effective for apps with variable traffic. Build the Lovable project image, push to Google Artifact Registry, and deploy to Cloud Run with a single command.

Lovable Prompt

Help me write a Cloud Build configuration to automatically build and deploy my Lovable project to Google Cloud Run when I push to the main GitHub branch.

Copy this prompt to try it in Lovable

Troubleshooting

Docker build fails with 'VITE_SUPABASE_URL is not defined' or blank pages after deployment

Cause: Vite environment variables are embedded at build time. If the --build-arg values were not passed during docker build, or they were passed as runtime environment variables (docker run -e), they will be undefined in the compiled bundle.

Solution: Always pass Supabase credentials as --build-arg during docker build, not as -e during docker run. Runtime environment variables injected with docker run are not visible to the Vite build process. Rebuild the image with the correct --build-arg flags.

typescript
1docker build --build-arg VITE_SUPABASE_URL=https://xxx.supabase.co --build-arg VITE_SUPABASE_ANON_KEY=your-key -t my-app .

Navigating directly to a route like /dashboard returns a 404 page

Cause: The nginx.conf file is missing or does not include the try_files $uri $uri/ /index.html directive needed for SPA routing.

Solution: Verify that nginx.conf exists in your project root, is copied into the Dockerfile with COPY nginx.conf /etc/nginx/conf.d/default.conf, and contains the try_files fallback. Rebuild the Docker image after updating the nginx config.

docker compose up fails with 'port is already allocated' error

Cause: Another process (likely npm run dev running directly) is already using port 5173 or 8080 on your host machine.

Solution: Stop the conflicting process — find it with lsof -i :5173 on macOS/Linux. Alternatively, change the host port in docker-compose.yml from 5173:5173 to a different port like 3000:5173.

Changes to source files are not reflected in the browser when using docker-compose

Cause: The node_modules volume override is not working correctly, or the --host flag was not passed to the Vite dev server.

Solution: Make sure the docker-compose.yml command includes -- --host after npm run dev. Also confirm the volumes section has both the project mount (.: /app) and the node_modules override (/app/node_modules) — the latter prevents the container's node_modules from being overwritten by the host's (potentially empty or incompatible) node_modules folder.

Best practices

  • Always use multi-stage Docker builds for Vite/React projects — the final nginx image should never contain Node.js or node_modules, keeping image sizes under 60 MB.
  • Pass Supabase credentials as build arguments during docker build, not as runtime environment variables — Vite embeds VITE_-prefixed variables into the JavaScript bundle at build time.
  • Add a .dockerignore file listing node_modules, .git, dist, and .env.local to prevent unnecessary files from being sent to the Docker build context, which speeds up builds significantly.
  • Use nginx for serving the static Vite output in production — never use node serve or http-server in production Docker images as they lack performance and security hardening.
  • Tag production images with semantic version numbers (my-app:1.2.3) rather than only 'latest' — this makes rollback straightforward and prevents confusion about which version is running.
  • For teams, push Docker images to a private container registry (AWS ECR, Google Artifact Registry, or GitHub Container Registry) rather than rebuilding on the deployment server.
  • Test the production Docker image locally with docker run before pushing to a cloud registry — catching nginx config or environment variable issues locally is far faster than debugging in a cloud environment.

Alternatives

Frequently asked questions

Does Lovable support Docker natively?

Lovable does not have a built-in Docker integration. However, because Lovable generates standard Vite+React+TypeScript projects with no proprietary dependencies, you can containerize the project yourself using the GitHub export. The workflow is: connect Lovable to GitHub, clone the repo, write a Dockerfile, and deploy the container anywhere you like.

Why do my Supabase credentials need to be build arguments rather than runtime environment variables?

Vite processes all environment variables prefixed with VITE_ at build time and embeds them directly into the compiled JavaScript bundle. By the time the container is running, the bundle is already compiled and static — there is no Node.js process to read runtime environment variables. Build arguments (--build-arg) are available during the npm run build step when Vite can embed them.

How large will the Docker image be?

A properly configured multi-stage Dockerfile produces a final nginx image typically between 20 MB and 60 MB, depending on the size of your compiled assets. The builder stage is much larger (400-600 MB with node_modules) but is discarded and not part of the final image.

Can I deploy the Lovable Docker container to Kubernetes?

Yes. The nginx static-serving container runs well in Kubernetes. You would create a Deployment manifest pointing to your container registry image, a Service to expose it, and an Ingress for routing. For environment variables, use Kubernetes Secrets or ConfigMaps to inject the Supabase build args during your CI/CD pipeline build step before pushing to the registry.

What is the difference between deploying with Docker versus using Lovable Cloud?

Lovable Cloud is the simplest option — one click, no configuration, managed globally. Docker gives you full control over the hosting environment: choose your cloud provider, region, server size, and network configuration. Docker is the right choice when you have compliance requirements, need specific data residency, or want to integrate with existing infrastructure that already uses containers.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.