Deployments

Push to your branch and Temps takes it from there — cloning the repo, building a Docker image with Nixpacks, starting the container, running health checks every 5 seconds, and switching traffic only after 2 consecutive successful checks. No config files needed. No downtime.


What is a deployment

A deployment is a single build-and-deploy cycle that takes your code from a Git commit (or a Docker image, or a static bundle) to a running application serving traffic. Each deployment is immutable — once created, it represents a specific version of your application at a specific point in time.

A deployment record tracks:

  • The source — which commit, branch, image, or bundle was deployed
  • The environment — which environment (production, staging, preview) received the deployment
  • The status — whether the deployment succeeded, failed, or is still running
  • The timeline — when each stage started and finished
  • The logs — full build and deploy output for every job in the pipeline
  • The container — the Docker image and running container(s) created by this deployment

Deployments are immutable. You cannot modify a completed deployment. To change what is running, you create a new deployment (by pushing code, triggering a pipeline, or rolling back).


The deployment pipeline

When a deployment is triggered, it flows through a series of jobs.

Branch deletes are ignored. When you delete a Git branch on GitHub or GitLab, the platform sends a push webhook where the after SHA is 40 zeros (the null SHA). Temps detects this before any pipeline work starts and discards the event — no deployment is created, no failure is logged. If you delete a branch and see nothing in the deployment list, this is expected behavior.

Loading diagram...

1. Clone repository

Temps clones your Git repository at the specified commit. For monorepos, only the configured app directory is relevant to the build.

GitLab archive downloads. GitLab serves tarball downloads through a redirect to a separate codeload host. Temps follows that single redirect automatically, but strips the authentication token from the redirected request and validates the redirect destination against SSRF guards before following it. This means cloning works transparently for GitLab repositories without any extra configuration, while preventing a maliciously crafted redirect URL from causing the server to make requests to internal infrastructure.

2. Build image

Temps detects your framework and builds a Docker image. The build method depends on your project:

  • Nixpacks (default for most frameworks) — Automatically detects language, dependencies, build command, and start command. No Dockerfile needed.
  • Dockerfile — If a Dockerfile exists in the repo root (or app directory), Temps uses it directly.
  • Static preset — For static sites, Temps builds the files and serves them with a lightweight web server.

3. Deploy container

The built image is started as a Docker container with:

  • The environment's resource limits (CPU, memory)
  • All environment variables (user-defined + auto-injected + service credentials)
  • Network connectivity to linked managed services
  • The configured replica count

4. Health check

Temps sends HTTP GET requests to the container every 5 seconds to verify it started successfully. The container must respond with a 2xx status code. See How do health checks work for the exact retry behavior.

5. Route traffic

Once the health check passes, the reverse proxy (Pingora, Cloudflare's open-source Rust proxy) is updated to route incoming requests to the new container. The old container continues serving requests until the switch is complete.

6. Mark complete

The deployment status changes to completed. Post-deploy tasks run in the background:

  • Screenshot capture (for the deployment preview in the dashboard)
  • Vulnerability scanning (Trivy 0.58.1 — Critical and High CVEs only)
  • Source map upload (for error tracking stack traces)
  • Cron job configuration

How do health checks work

Temps requires 2 consecutive successful HTTP responses before routing traffic to a new container, and will automatically roll back if errors persist for 60 seconds.

The health check loop runs as follows (crates/temps-deployments/src/jobs/deploy_image.rs):

ParameterValueNotes
Check interval5 secondsBetween each HTTP GET attempt
Request timeout5 secondsPer individual request
Required successes2 consecutiveBoth must be 2xx — a single failure resets the counter
Max error window60 secondsIf errors persist this long, deployment is marked failed
Max total wait300 seconds (5 min)Configurable per project

What "2 consecutive successes" means in practice: if the container returns a 200 on check 7 but a 500 on check 8, the counter resets to 0 and must reach 2 again before traffic switches. This prevents routing traffic to a container that is flapping.

Automatic rollback: if the container produces errors continuously for 60 seconds (the max_error_duration), the deployment fails and the previous deployment's container continues serving traffic. Users see no downtime — the old version stays live throughout.

You can configure the health check path in project settings. Setting the path to null disables HTTP health checks entirely (useful for background workers and queue processors that don't listen on a port).


Deployment states

StateMeaning
pendingDeployment is queued, waiting for a worker
runningBuild or deploy step is actively executing
deployingContainer is starting and waiting for health checks
readyContainer is healthy and receiving traffic
completed / deployedFully deployed and serving traffic
failedA job in the pipeline failed. Check logs for details.
cancelledManually cancelled by a user before completion
pausedTemporarily paused (can be resumed)
stoppedContainer was stopped after being deployed

A deployment can only be cancelled while in pending or running state. Once a deployment reaches completed, it stays in that state until a new deployment replaces it.


Jobs and stages

Each deployment is composed of jobs — discrete units of work that execute in a defined order with dependency tracking.

Job typeWhat it does
DownloadRepoJobClones the Git repository at the target commit
BuildImageJobBuilds the Docker image using Nixpacks or Dockerfile
DeployImageJobStarts the container, runs health checks, routes traffic
DeployStaticJobDeploys pre-built static files
DeployStaticBundleJobDeploys an uploaded static bundle
PullExternalImageJobPulls a Docker image from a registry (supports private registries with auth)
VerifyLocalImageJobVerifies a Docker image exists locally (for rollbacks)
MarkDeploymentCompleteJobFinal status update and cleanup
ConfigureCronsJobSets up scheduled tasks
TakeScreenshotJobCaptures a visual preview of the deployed site
ScanVulnerabilitiesJobRuns Trivy security scanning on the container image
CaptureSourceMapsJobUploads source maps for error tracking

Each job has:

  • A status (Pending, Running, Success, Failure, Cancelled)
  • An execution order and dependencies (jobs that must complete first)
  • A log stream with timestamped output
  • An error message if it failed

If a job fails, all dependent jobs are automatically cancelled.


Build detection

Temps auto-detects your framework based on files in the repository:

Detection signalFrameworkBuild behavior
next.config.js or next.config.tsNext.jsNixpacks with Next.js preset
vite.config.tsVite (React, Vue, Svelte)Nixpacks, output served as static
DockerfileDockerDirect Docker build
package.json with start scriptNode.jsNixpacks with Node.js preset
requirements.txt or pyproject.tomlPythonNixpacks with Python preset
go.modGoNixpacks with Go preset
Cargo.tomlRustNixpacks with Rust preset
index.html (no framework files)StaticServed directly

You can override the auto-detected preset in project settings if the detection is wrong.


Zero-downtime deploys

Temps uses a blue-green deployment strategy:

  1. The new container starts alongside the existing one
  2. Health checks verify the new container is ready (2 consecutive 2xx responses, checked every 5s)
  3. The reverse proxy (Pingora) switches traffic to the new container atomically
  4. The old container is stopped and removed

During the transition, both containers are running. Requests in flight to the old container complete normally. New requests go to the new container. There is no downtime visible to users.

If the new container fails its health check or produces errors for 60 seconds continuously, the old container continues serving traffic and the deployment is marked as failed.


Deployment types

TypeTriggered byBuild step
Git pushWebhook from GitHub/GitLab when you push to a tracked branchFull build from source
Manual triggerDashboard "Redeploy" button or trigger-pipeline APIFull build from source
Docker imagedeploy/image API endpointNo build — pulls and deploys the image
Image uploaddeploy/image-upload API endpointNo build — loads the uploaded tarball
Static bundledeploy/static API endpointNo build — serves the uploaded files
RollbackDashboard rollback action or rollback API endpointNo build — reuses the Docker image from a previous deployment

Rollbacks are the fastest because they skip both the clone and build steps. They reuse the existing Docker image from a previous deployment, so they complete in seconds rather than minutes.


Viewing logs for previous deployments

Every deployment — including completed, failed, cancelled, and rolled-back ones — retains its full log output. Logs are stored per-job in JSONL format and are accessible as long as the deployment record exists. You do not need the deployment to be active or currently serving traffic to read its logs.

View build and deploy logs for a specific deployment

  1. 1

    Open your project in the dashboard and click Deployments in the sidebar.

  2. 2

    The list shows all deployments for the project, including completed and failed ones. Click a deployment row to open its detail page.

  3. 3

    On the deployment detail page, each job in the pipeline (Clone, Build, Deploy, Health Check, etc.) shows its log output inline. Expand a job to read its full log.

    Checkpoint: Confirm you can see log output for each job — the page title should show the deployment ID and status, confirming you are viewing the correct historical deployment.

Listing historical deployments

To find a previous deployment's ID, list all deployments for a project:

bunx @temps-sdk/cli deployments list --project my-app

Filter by environment to narrow the results:

bunx @temps-sdk/cli deployments list --project my-app --environment production

Add --json to get machine-readable output for scripting:

bunx @temps-sdk/cli deployments list --project my-app --json

Once you have the deployment ID, fetch its logs:

# Show the last 100 lines from each job in deployment 1234
bunx @temps-sdk/cli deployments logs --project my-app --deployment 1234

# Increase the line limit
bunx @temps-sdk/cli deployments logs --project my-app --deployment 1234 --lines 500

# Follow logs for a deployment that is still running
bunx @temps-sdk/cli deployments logs --project my-app --deployment 1234 --follow

If --deployment is omitted, the command defaults to the most recent deployment for the specified environment.

What is retained in historical logs

Each deployment job writes structured JSONL log entries that include:

  • A timestamp for each line
  • A severity level (info, success, warning, error)
  • The raw output from the build tool (Nixpacks, Docker, or the health-check runner)

This means you can retrieve the exact build output from a failed deployment days after it occurred — useful for debugging intermittent build failures or comparing the build environment between a working and a broken deploy.

Access control for deployment logs

Deployment logs are scoped to the project they belong to. The same permissions that govern whether a user can view a project's deployments also control access to its logs — there is no separate log-access permission.

RoleCan list deploymentsCan read logs
OwnerYesYes
AdminYesYes
MemberYesYes
Viewer (read-only)YesYes
No project accessNoNo

Logs are not publicly accessible. All API requests for deployment logs require a valid session token or API key scoped to the project. If you use API keys for CI/CD pipelines that retrieve logs, generate a dedicated key with the minimum required scope rather than reusing a personal token.

Was this page helpful?