NexosNexos

Deployments

Every git push to a connected repository triggers a deployment. This page explains what happens, the two deploy modes, how preview URLs are shaped, and the actions you can take on a running environment.

Quick recap. A deployment is one git push. An environment is one branch. The first push to a branch creates the environment (brand new). Every push after that updates it (incremental).

From git push to live URL

  1. You git push to any branch.
  2. GitHub delivers a webhook to POST /webhooks/github.
  3. Nexos verifies the HMAC-SHA256 signature, finds the matching project, and decides whether this is a brand new or incremental deploy based on whether an environment already exists for the branch.
  4. A build job lands on the BullMQ build queue.
  5. The scheduler checks your quota, picks a provider node with free capacity (preferring one that has built this project before for cache affinity), and dispatches the job over gRPC.
  6. The agent on the selected node builds your image with BuildKit, starts the containers, and streams logs back. Nexos wires Caddy to route your preview URL to the new container.

For most apps this takes 30–60 seconds. You can watch every step stream into the dashboard in real time.

Brand new vs. incremental deploys

The single most important concept on Nexos: we know the difference between “this is a new branch” and “this is another push to a branch I'm already running.”

Brand new deployments

Fires the first time Nexos sees a branch. Full stack gets provisioned from scratch:

  • App container built from your Dockerfile
  • Fresh Postgres container (empty)
  • Fresh Redis container
  • New Caddy route for the preview URL
  • Both on_deploy and on_new hooks run

Expect this to take longer than an incremental — you're paying for cold starts.

Incremental deployments

Fires on subsequent pushes to a branch that's already running. Only the app container is rebuilt:

  • App image rebuilt (with BuildKit layer cache — often fast)
  • Old app container stopped
  • New app container started, pointed at the same database, Redis, and S3 bucket
  • Only the on_deploy hook runs

Your database persists across incremental deploys. That's the whole point: test data survives, user sessions survive, multi-step workflows survive. Fix a bug on branch, push, keep testing.

Environment states

An environment moves through these states over its lifetime:

StateWhat's happening
provisioningScheduler picked a node, containers are being allocated.
buildingBuildKit is building your image on the assigned node.
deployingContainers are starting. Health checks are running.
runningLive at the preview URL. Ready for traffic.
pausedApp container stopped. Database and storage persist.
failedBuild or deploy failed. Check build logs for the reason.
destroyedAll resources released. Environment can't be resumed.

Preview URLs

Preview URLs follow a predictable pattern:

{branch-slug}-{project-slug}.{platform-domain}

Example: project my-app, branch feature/login-page

feature-login-page-my-app.nexos.dev

Branch names are slugified: slashes become hyphens, special characters are stripped, and the whole thing is lowercased. Because URLs are deterministic you can share them in PR descriptions, Slack messages, or automation — they'll still point to the same environment on every push.

Build logs

Build output streams into the dashboard over WebSocket. Open your environment and click Logs to watch BuildKit, container startup, and hook execution live.

Logs are also available programmatically at GET /api/deployments/:id/logs — useful for CI bots and Slack integrations.

Environment actions

From the dashboard or API you can act on a running environment in several ways:

  • Restart — stops the app container and runs a fresh incremental deploy from the latest commit. Database stays intact. Use this to re-run startup or pull in a new env var.
  • Pause — stops the app container. Database and storage keep persisting. Environment enters paused. Useful for freeing quota on idle previews.
  • Resume — kicks off an incremental deploy from the latest commit and reconnects to the existing database. Back to running.
  • Reset Database — destroys the current database and reprovisions an empty one. All environment data is lost.
  • Destroy — tears down every container, storage, and Caddy route. Environment enters destroyed and can't be brought back.