Deployments
A Temps deployment runs in five steps: clone the commit, build a Docker image (your Dockerfile, or one Temps generates for your framework), start the new container, health-check it every 5 seconds, and switch traffic only after 2 consecutive successful checks. If the new container never becomes healthy, the deploy fails and the previous deployment keeps serving. Every deployment in a project belongs to one environment and keeps its own logs, image and status.
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 your provider, 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.
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:
- Framework or language preset — Temps generates a Dockerfile for the detected (or chosen) preset; frameworks without their own preset build through autopack, Temps' built-in builder. No Dockerfile needed.
- Dockerfile — If a
Dockerfileexists in the repo root (or app directory), Temps can use it directly, with BuildKit layer caching. - Static preset — For static sites, Temps builds the files, extracts the output and serves it from the proxy.
Build arguments: your environment variables are passed to the build as ARG values (--build-arg), so build-time configuration such as NEXT_PUBLIC_* variables works without extra setup. Secrets are encrypted at rest and are not baked into the image unless your Dockerfile copies them in.
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 + credentials of linked services)
- 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 fails the deployment if it isn't healthy within the startup timeout (5 minutes by default) — the previous deployment keeps serving the whole time.
The health check loop runs as follows (crates/temps-deployments/src/jobs/deploy_image.rs):
| Parameter | Value | Notes |
|---|---|---|
| Check interval | 5 seconds | Between each HTTP GET attempt |
| Request timeout | 5 seconds | Per individual request |
| Required successes | 2 consecutive | Each must be 2xx, 3xx, 404 or 405 — a single failure resets the counter |
| Startup timeout | 300 seconds (5 min) by default | Configurable from 30 to 3600 seconds under Project → Settings → Build & deploy (Startup timeout (seconds)). Connection errors and 4xx/5xx responses are retried until then; the deploy fails if the container isn't healthy by then |
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.
Failed health checks don't cause downtime: if the container hasn't passed its health check by the startup timeout, the deployment fails and the previous deployment's container continues serving traffic. This protection applies only before cutover — once a deployment has succeeded, nothing rolls it back automatically. To go back to an earlier version, roll back manually.
The health check probes / by default. To probe a different endpoint, set health.path in a .temps.yaml file in your repository.
Deployment states
The pipeline engine moves a deployment through six build-and-release states — pending, running, built, completed, failed and cancelled. Operational actions can also persist states outside the pipeline: pause, resume, rollback, teardown and supersession write paused, deployed and stopped.
| State | Meaning |
|---|---|
pending | Deployment is queued, waiting for a worker |
running | Build or deploy step is actively executing |
built | Build step finished; deploy step has not yet completed |
completed | Fully deployed and serving traffic |
failed | A job in the pipeline failed. Check logs for details. |
cancelled | Manually cancelled by a user before completion |
paused | Manually paused; can be resumed |
deployed | Resumed from paused, or the state a rollback restores |
stopped | Containers and resources were torn down, including when a newer successful deployment supersedes this one |
A deployment can only be cancelled while in pending or running state. The normal pause/resume path moves a live deployment from completed or deployed to paused, then back to deployed. Teardown or supersession moves a deployment to stopped. If a pipeline job fails, the deployment becomes failed and its detail page shows the failing job — see Debug a failed deployment. See the CLI reference for deployments pause/deployments resume.
Jobs and stages
Each deployment is composed of jobs — discrete units of work that execute in a defined order with dependency tracking.
| Job type | What it does |
|---|---|
DownloadRepoJob | Clones the Git repository at the target commit |
BuildImageJob | Builds the Docker image from your Dockerfile or a generated one |
DeployImageJob | Starts the container, runs health checks, routes traffic |
DeployStaticJob | Deploys pre-built static files |
DeployStaticBundleJob | Deploys an uploaded static bundle |
PullExternalImageJob | Pulls a Docker image from a registry (supports private registries with auth) |
VerifyLocalImageJob | Verifies a Docker image exists locally (for rollbacks) |
MarkDeploymentCompleteJob | Final status update and cleanup |
ConfigureCronsJob | Sets up scheduled tasks |
TakeScreenshotJob | Captures a visual preview of the deployed site |
ScanVulnerabilitiesJob | Runs Trivy security scanning on the container image |
CaptureSourceMapsJob | Uploads source maps for error tracking |
Each job has:
- A status (Pending, Waiting, Running, Success, Failure, Cancelled, Skipped)
- 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 picks a preset from files in the repository:
| Detection signal | Preset | Build behavior |
|---|---|---|
docker-compose.yml / compose.yaml | Docker Compose | Runs the stack with docker compose up |
Dockerfile | Dockerfile | Direct Docker build |
next.config.js / .mjs / .ts | Next.js | Server (SSR) image |
vite.config.js / .ts | Vite | Static output served from the proxy |
rsbuild.config.ts, docusaurus.config.* | Rsbuild, Docusaurus | Static output |
requirements.txt, pyproject.toml, setup.py, Pipfile | Python | Server image |
go.mod, Cargo.toml, pom.xml / build.gradle | Go, Rust, Java | Server image |
Express, NestJS, Astro, Nuxt, plain Node.js, Laravel and Rails are not detected from files — choose a preset such as nixpacks-node, nixpacks-php or nixpacks-ruby on the project. Create React App isn't detected either; choose the react-app preset. If nothing matches, the build fails and asks you to pick a preset. You can override the detected preset in project settings. See Supported frameworks, languages and build methods for the full list and port defaults.
What you can control
- Build — use your own Dockerfile or a preset, override the install, build and start commands, and set the app directory for monorepos (build configuration).
- Environment variables — defined on the project and assigned to one or more environments; values are encrypted at rest and applied on the next deployment (manage environment variables).
- Resources — CPU and memory limits and replicas per environment (resources, scaling).
- Port and health check — the port your app listens on (port resolution), and the health-check path in
.temps.yaml. - Domains — custom domains with automatic Let's Encrypt certificates (add a custom domain).
Zero-downtime deploys
Temps uses a blue-green deployment strategy:
- The new container starts alongside the existing one
- Health checks verify the new container is ready (2 consecutive 2xx responses, checked every 5s)
- The reverse proxy (Pingora) switches traffic to the new container atomically
- 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 doesn't pass its health check before the startup timeout (5 minutes by default), the old container continues serving traffic and the deployment is marked as failed.
Rollback
A rollback is a manual redeploy of a previous deployment. It reuses that deployment's Docker image, so there is no clone or build step, and it goes through the same health check and traffic switch as any other deploy. Temps does not roll back on its own after a deployment has succeeded — if a release misbehaves in production, trigger the rollback from the dashboard, the API or the CLI. See Rollbacks for the steps.
Build failures
The most common reasons a deployment fails:
| Symptom | Likely cause | Fix |
|---|---|---|
Could not auto-detect project type | No detection signal in the repo | Choose a preset, or add a Dockerfile |
| Dependency install fails | Missing or inconsistent lock file / manifest | Commit the lock file; check package.json, requirements.txt, go.mod |
| Health check never passes | App listens on the wrong port or binds to 127.0.0.1 | Bind to 0.0.0.0 and set the project's port to the one your app uses |
| Container exits with code 137 | Out of memory | Raise the memory limit (resources) |
For a step-by-step walkthrough, see Debug a failed deployment and Troubleshooting.
Deployment types
| Type | Triggered by | Build step |
|---|---|---|
| Git push | Webhook from your Git provider when you push to a tracked branch | Full build from source |
| Manual trigger | Dashboard "Redeploy" button or trigger-pipeline API | Full build from source |
| Docker image | deploy/image API endpoint | No build — pulls and deploys the image |
| Image upload | deploy/image-upload API endpoint | No build — loads the uploaded tarball |
| Static bundle | deploy/static API endpoint | No build — serves the uploaded files |
| Rollback | Dashboard rollback action or rollback API endpoint | No 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
Open your project in the dashboard and click Deployments in the sidebar.
- 2
The list shows all deployments for the project, including completed and failed ones. Click a deployment row to open its detail page.
- 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 (Docker BuildKit) 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. Project roles are described in Manage team access. For application (runtime) logs, see Logs.
| Role | Can list deployments | Can read logs |
|---|---|---|
| Owner | Yes | Yes |
| Admin | Yes | Yes |
| Deployer | Yes | Yes |
| Viewer (read-only) | Yes | Yes |
| No project access | No | No |
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.