Deployments
Web Applications
Deploy web applications from a Git repository. ZevCloud supports any language or framework that can run in a container.
Supported Frameworks
ZevCloud uses Nixpacks to auto-detect and build your application:
| Framework | Auto-Detected | Default Port |
|---|---|---|
| Next.js | ✅ | 3000 |
| React / Vue (Vite) | ✅ | Served as a static site (no port) |
| Express / Node.js | ✅ | 3000 |
| NestJS | ✅ | 3000 |
| FastAPI | ✅ | 8000 |
| Django / Flask | ✅ | 8000 |
| Go | ✅ | 8080 |
| Ruby on Rails | ✅ | 3000 |
| PHP / Laravel | ✅ | 8000 |
| Rust | ✅ | 8080 |
| Static HTML | ✅ | 80 |
Deploying from GitHub
Public Repositories
- Add Service → Web Application → Public Repository
- Paste the repository URL (e.g.,
https://github.com/user/repo) - Select the branch
- Configure and deploy
Private Repositories
- Add Service → Web Application → Import from GitHub
- If not connected, click “Connect GitHub” to install the ZevCloud GitHub App
- Select your repository from the list
- Select the branch
- Configure and deploy
Build Configuration
ZevCloud auto-detects most settings, but you can customize:
| Setting | Description | Example |
|---|---|---|
| Build Command | Command to build your app | npm run build |
| Install Command | Command to install dependencies | npm ci |
| Start Command | Command to start your app | npm start |
| Output Directory | For static builds | dist, .next, build |
| Root Directory | If your app is in a subdirectory | packages/web |
| Port | Port your app listens on | 3000 |
These can be set during creation or updated in Service Settings. Root and output directories can be written with or without a leading slash: dist and /dist mean the same folder. Root Directory is a dropdown of the folders in your repository, so you pick the path rather than typing it — a mistyped one fails the build several minutes in, with an error about the build tool rather than the path.
If your repository has its own Dockerfile, ZevCloud uses it instead of building your app automatically. See Using a Dockerfile.
Command rules
Install, build and start commands can chain steps with && or ||, for example npm ci && npm run build. They can’t contain:
;, a single|or a single&- redirects (
>or<) - backticks or parentheses
$VARIABLESoutside single quotes
If a step needs any of these, put the logic in a script (for example a start script in package.json) and run that script as the command. Saving a command that breaks these rules fails with an error naming the character, such as “Your start command can’t contain …”.
Your app still sees every environment variable at runtime. Read them in your code (for example process.env.PORT) rather than in the command.
Static Sites & SPAs
Client-rendered apps (Vite, React, Vue, plain HTML) don’t need a server process. Their build produces a folder of static files — the right way to serve them is as a static site.
When you create a service from a Vite project, ZevCloud detects this and pre-selects static serving. You can also change it any time in Service Settings → Build & Deploy:
| Setting | What it does |
|---|---|
| Static site | Builds your app, then serves the output folder as files. No server process, no port to configure. |
| SPA (Single Page Application) | Serves index.html for unmatched routes, so client-side routing (React Router, Vue Router) works on page refresh and direct links. |
| Publish Directory | The folder your build produces. Vite uses dist; Create React App uses build. |
Common symptoms and their fix:
- Deploy succeeds but the site is blank or 404s — the Publish Directory doesn’t match your build output. Check what folder your build command creates.
- Pages 404 on refresh or direct links — enable the SPA toggle so unmatched routes fall back to
index.html. - Deploy fails with a port or health check error on a Vite/React app — enable Static site. A client-rendered app has no server listening on a port, so port-based deploys can’t succeed.
Changes take effect on the next deploy — the dashboard offers a redeploy when you save.
Monorepos
Create one service per app. How you point each service at its app depends on whether the repository is a workspace.
Independent folders
If each app has its own package.json (or requirements.txt, go.mod) and they don’t depend on each other, set each service’s Root Directory to its folder:
repo/
frontend/ → service 1, Root Directory: frontend
backend/ → service 2, Root Directory: backendNothing else to configure. This is the common case.
Workspaces (pnpm, Yarn, npm, Turborepo, Nx)
If your repository has a turbo.json, a pnpm-workspace.yaml, an nx.json, or a workspaces field in the root package.json, the apps share dependencies through the repository root. Pointing the Root Directory at the app folder will fail the install, because dependencies declared as workspace:* can only be resolved from the root.
Leave the Root Directory at / and filter the build to one app instead:
# pnpm + Turborepo
pnpm install --frozen-lockfile
pnpm turbo run build --filter=web...
pnpm --filter web start
# npm workspaces
npm ci
npm run build --workspace=web
npm start --workspace=web
# Yarn workspaces
yarn install
yarn workspace web build
yarn workspace web startThe trailing ... in a Turborepo filter builds that app’s own workspace dependencies first. Without it, shared packages the app imports won’t be built.
When you select a repository, ZevCloud detects the workspace and offers these commands filled in with your package name — one click applies them.
Auto-deploy is folder-aware
For services with a Root Directory set, pushes only redeploy the services whose folder contains changed files. A commit that only touches frontend/ won’t rebuild your backend. Changes to shared files outside every service folder — a root package.json, a shared packages/ directory — redeploy all services from that repository, since they can affect any build.
Environment Variables
Set environment variables in the Variables tab of your service:
DATABASE_URL=postgres://user:pass@host:5432/db
NODE_ENV=production
API_KEY=sk_live_...Variables marked as secret are encrypted at rest and hidden in the UI.
Auto-Deploy on Push
When you push to the configured branch, ZevCloud automatically:
- Receives a GitHub webhook
- Clones the latest code
- Builds using Nixpacks
- Deploys with zero-downtime rollout
- Sends an email notification (success or failure)
Disable auto-deploy in Service Settings → Build & Deploy.
Build Logs
View real-time build logs in the Deployments tab. Each deployment shows:
- Git commit message and SHA
- Build duration
- Success or failure status
- Detailed build output
How Deploys Work explains each build phase, what’s cached between deploys, and how to read a failed build back to the step it failed in.
Resource Limits
Each plan provides different resource limits:
| Plan | Memory | CPU | Build Timeout |
|---|---|---|---|
| Free | 512 MB | 1 core | 10 min |
| Starter | 2 GB | 1 core | 20 min |
| Pro | 4 GB | 2 cores | 30 min |
| Business | 8 GB | 4 cores | 60 min |
Persistent volumes (beta)
A deploy builds a fresh container from your code, so anything your app wrote to its own filesystem is gone after the next deploy or restart. That is the right default for code, and the wrong one for data: SQLite files, user uploads, generated reports, a local cache you want to keep.
A persistent volume is a folder that survives deploys and restarts. You pick the path inside the container, and whatever your app writes there stays.
To attach one: open the service, go to the Volumes tab, enter the folder your app writes to (for example /app/data or /app/uploads) and click Attach volume. The service restarts once so the folder is mounted. From then on every deploy and restart finds the same files there.
A few things to know:
- Paid plans only. Each plan includes a total allowance in GB across all volumes on one service. The Volumes tab shows how much is used; we email you when a service gets close to its allowance, and the dashboard turns amber at 80%. Nothing is paused when you reach it, but writes fail once the disk is actually full, so give yourself room.
- Up to three volumes per service. One for data and one for uploads covers most apps. Mount paths cannot overlap.
- The mount hides what was there. An empty volume mounted at
/app/datacovers any/app/datathat came from the build. Mount at a folder your app creates at runtime, not one that ships in your repository. You cannot mount over/appitself or the container’s system folders. - Removing a volume deletes its files. There is no undo, and the service restarts afterwards. Download anything you need first (the Terminal tab can
tara folder, or your app can export it). - One service, one volume. Volumes are not shared between services. If two services need the same files, use a storage bucket instead.
Outbound network restrictions
To keep the platform clean for everyone, customer containers are not allowed to make outbound connections on a few specific ports. The host blocks them at the firewall, so any code in your app that tries to reach these ports will time out.
- SMTP (25, 465, 587, 2525) — direct mail submission. Use a transactional email API (Resend, Mailgun, Postmark, SendGrid) over HTTPS instead. Their REST/HTTP endpoints are not blocked.
- IRC (6667, 6697) — typical command-and-control channels.
- Mining-pool stratum (3333, 4444, 5555, 7777, 14444) and Bitcoin RPC (8332, 8333).
Everything else (HTTPS to third-party APIs, your database, Redis, CDNs, webhooks) works normally. If you need to send email, switch to your provider’s HTTPS API.
Rollback
If a deploy causes problems, roll back to an earlier one from the Deployments tab.
- Instant rollback. Recent deployments whose build is still stored roll back instantly. Nothing is rebuilt: the stored build starts again, usually within seconds.
- Older deployments. Older builds may no longer be stored. Apps built with a Dockerfile rebuild the old commit instead, which takes as long as a normal build. Other apps can’t roll back that far: push a revert commit to the branch and let it deploy.
Cancelling a build
A queued or running deployment can be cancelled from the Deployments tab. Your app keeps serving its previous version, so cancelling is safe at any point.
Crash-loop protection
If your app crashes 10 times in a row while starting, ZevCloud stops it automatically and emails the team owner. The dashboard shows why the app was stopped.
To recover, find the cause in the Logs tab (it is usually the last few lines before each crash), fix it, and redeploy. A successful redeploy clears the stop.
Updated at, Monday, October 26, 2026