What You Will Build
A production-ready Sandpack bundler service running in Docker behind an Nginx reverse proxy with a TLS certificate. Once deployed, any Sandpack-powered application can point its bundlerURL at your domain instead of CodeSandbox's servers.
Prerequisites
- A RamNode VPS running Ubuntu 22.04 LTS with at least 1 GB RAM and 10 GB disk
- A domain name or subdomain pointed at your VPS IP (e.g.,
sandbox.yourdomain.com) - Root or sudo access
- SSH access to the server
Need help with initial setup? See our Initial Server Setup for Ubuntu guide.
Update the System and Install Docker
Start by logging into your VPS and bringing the package list up to date.
sudo apt update && sudo apt upgrade -yInstall Docker using the official convenience script:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp dockerVerify Docker is running:
docker --version
docker run hello-worldYou should see Hello from Docker! confirming the daemon is active.
Pull and Run the Sandpack Bundler Container
The LibreChat team maintains a pre-built Docker image of the CodeSandbox bundler that packages an optimized Nginx configuration alongside the compiled bundler static files.
docker pull ghcr.io/librechat-ai/codesandbox-client/bundler:latestRun the container on port 8080:
docker run -d \
--name sandpack-bundler \
--restart unless-stopped \
-p 8080:80 \
ghcr.io/librechat-ai/codesandbox-client/bundler:latestConfirm the container is running:
docker psTest it locally:
curl -I http://localhost:8080A 200 OK response confirms the bundler is serving files correctly.
Install Nginx and Certbot
The bundler needs to be accessible over HTTPS on a public domain. Install Nginx and Certbot:
sudo apt install -y nginx certbot python3-certbot-nginxConfigure the Nginx Reverse Proxy
Create a new Nginx server block for your sandpack subdomain:
sudo nano /etc/nginx/sites-available/sandpackPaste the following configuration, replacing sandbox.yourdomain.com with your actual subdomain:
server {
listen 80;
server_name sandbox.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS headers — tighten the origin in production
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
# Cache static bundler assets aggressively
proxy_cache_bypass $http_upgrade;
expires 7d;
add_header Cache-Control "public, immutable";
}
}Enable the site and test the configuration:
sudo ln -s /etc/nginx/sites-available/sandpack /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxObtain a TLS Certificate
Browsers require HTTPS for Service Workers, which Sandpack relies on for caching transpilers. Run Certbot to obtain a free Let's Encrypt certificate:
sudo certbot --nginx -d sandbox.yourdomain.comChoose option 2 when asked whether to redirect HTTP to HTTPS — this ensures all traffic is encrypted.
Verify auto-renewal is set up:
sudo systemctl status certbot.timerVerify the Deployment
Open a browser and navigate to https://sandbox.yourdomain.com. You should see an Nginx response or blank page — this is expected. The bundler serves JavaScript assets, not a visual UI.
Confirm it is working by requesting a known asset path:
curl -I https://sandbox.yourdomain.com/static/js/sandbox.jsA 200 OK with Content-Type: application/javascript confirms the bundler is live and reachable.
Connect Your Application to the Self-Hosted Bundler
Any application using sandpack-react or sandpack-client can now point to your VPS. Update the bundlerURL option:
React (sandpack-react)
import { Sandpack } from "@codesandbox/sandpack-react";
export default function CodePlayground() {
return (
<Sandpack
template="react"
options={{
bundlerURL: "https://sandbox.yourdomain.com",
}}
/>
);
}Vanilla JS (sandpack-client)
import { SandpackClient } from "@codesandbox/sandpack-client";
const client = new SandpackClient(
iframeElement,
sandboxInfo,
{ bundlerURL: "https://sandbox.yourdomain.com" }
);LibreChat integration
If you are running a self-hosted LibreChat instance, add the following to your .env file:
SANDPACK_BUNDLER_URL=https://sandbox.yourdomain.comKeep the Container Updated
The bundler image is updated periodically. To pull the latest version and restart:
docker pull ghcr.io/librechat-ai/codesandbox-client/bundler:latest
docker stop sandpack-bundler
docker rm sandpack-bundler
docker run -d \
--name sandpack-bundler \
--restart unless-stopped \
-p 8080:80 \
ghcr.io/librechat-ai/codesandbox-client/bundler:latestYou can automate this with a cron job or a simple shell script scheduled via crontab -e.
Hardening CORS for Production
The configuration above uses Access-Control-Allow-Origin: *, which is fine for testing but overly permissive in production. Replace the wildcard with your application's origin:
add_header Access-Control-Allow-Origin "https://yourapp.com" always;If you need to allow multiple origins, use a conditional in Nginx:
set $cors_origin "";
if ($http_origin ~* "^https://(yourapp\.com|staging\.yourapp\.com)quot;) {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;Troubleshooting
Container exits immediately
Run docker logs sandpack-bundler to inspect the output. Missing port bindings are the most common cause.
CORS errors in the browser
Confirm the Access-Control-Allow-Origin header is present using curl -I -H "Origin: https://yourapp.com" https://sandbox.yourdomain.com. If the header is missing, check that Nginx has been reloaded after your config change.
Service Worker fails to register
This almost always means the bundler is being served over HTTP rather than HTTPS. Double-check that Certbot completed successfully and that the HTTPS redirect is active.
Bundler loads but code does not execute
The bundler fetches npm package tarballs from the public npm CDN at runtime. If your VPS is behind a restrictive firewall, ensure outbound traffic to registry.npmjs.org is permitted.
Resource Usage
On a RamNode 1GB VPS, the bundler container uses approximately 30–50 MB of RAM at idle and peaks around 80 MB under moderate concurrent load. The Docker image is roughly 45 MB compressed. This makes it viable to run alongside other services on even the smallest RamNode plan, though a 2GB instance gives comfortable headroom for co-hosting an application and its bundler on the same node.
What's Next
- Pin a specific image tag instead of
latestto lock bundler behavior across deployments - Add rate limiting in Nginx using
limit_req_zoneto prevent abuse on public-facing instances - Monitor with Uptime Kuma — see the RamNode Uptime Kuma deployment guide for a lightweight self-hosted monitoring setup
- Deploy LibreChat alongside this bundler to get a full self-hosted AI assistant with live code execution capabilities
