Set Up Preview Deployments
Preview deployments create a temporary environment for every non-production branch, giving you a live URL to review changes before merging. There is no default cap on the number of preview environments — teams with many open branches can push freely without hitting a ceiling.
Enable preview environments
Preview deployments are controlled at the project level.
Enable preview environments
- 1
Open your project in the dashboard.
- 2
Go to Settings > General.
- 3
Find Preview Environments and toggle it on.
- 4
Click Save.
Checkpoint: After saving, push to any branch other than main and confirm a new preview environment appears under the Environments tab.
- Open your project in the dashboard
- Go to Settings
- Find Preview Environments and toggle it on
- Click Save
Once enabled, any push to a branch that is not the project's main branch (usually main or master) will automatically create a preview environment and deploy to it.
The main branch is configured in your project settings under Git > Branch. Pushes to this branch deploy to the default (production) environment. All other branches create preview environments.
Run previews on-demand
Turn on on-demand previews
By default, every preview environment runs 24/7 just like production. You can instead have each newly created preview start in on-demand mode, where it scales to zero when idle and wakes on the next request — so a stack of stale feature-branch previews stops consuming resources.
Under Settings > General, below the Enable Preview Environments switch, you'll find:
| Setting | Default | Range | What it does |
|---|---|---|---|
| On-Demand Preview Environments | Off | — | When on, every newly auto-created preview starts in on-demand mode |
| Idle timeout (seconds) | 300 | 60–86400 | How long a preview sits idle before it scales to zero |
| Wake timeout (seconds) | 30 | 5–120 | Maximum time the proxy holds the first request while waiting for the container to become ready; the request receives a 503 if this expires |
The on-demand toggle is disabled until preview environments are enabled, and the two timeout inputs only appear once on-demand is turned on. Both inputs are validated client-side against the same ranges the backend enforces.
What happens when a sleeping preview receives its first request
When a sleeping preview receives its first request, Temps does not immediately reject it. The proxy holds the request in place and works through these steps:
- Container start is triggered. Temps instructs Docker to start the container for the sleeping environment.
- The proxy picks up the new address via an in-process route reload. Once the container is registered, Temps triggers a ForceRouteReload internally rather than waiting for a PostgreSQL notification cycle. This eliminates the delay that previously caused the first request to see a 503 for up to ~10 seconds while the proxy waited for a database notification to propagate.
- A TCP readiness probe confirms the container's port is open. Temps does not forward the held request until the container is actually accepting TCP connections on its exposed port.
- The held request is forwarded and the container responds normally. From the caller's perspective, the first request simply took longer than usual — there is no error response during the wake window.
If the wake timeout expires before the container passes the TCP probe, the held request receives a clean 503 Service Unavailable response. No internal error detail is exposed to the caller.
A successful first-request wake is transparent: the caller sees a longer initial response time (container cold-start), and all subsequent requests are served at normal latency. Once the container is up, it stays up until it goes idle again.
Set the wake timeout to a value higher than your container's typical cold-start time. Most Node.js or Go images start in well under 10 seconds; JVM-based applications may need 60 seconds or more. The default of 30 seconds covers the majority of use cases.
This setting is opt-in — it defaults to off, and it only applies to previews created after you enable it. Existing preview environments keep whatever configuration they already had. When on, Temps seeds each new preview with on-demand mode plus your idle/wake timeouts; CPU, memory, replicas, and security still inherit from the environment → project → global defaults.
You can also set this through the project-settings update API. The UpdateProjectSettingsRequest body accepts three optional fields:
curl -X POST "https://your-temps-instance/api/projects/{project_id}/settings" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"preview_envs_on_demand": true,
"preview_envs_idle_timeout_seconds": 300,
"preview_envs_wake_timeout_seconds": 30
}'
If a timeout falls outside its valid range, the request is rejected with a validation error (for example, preview_envs_idle_timeout_seconds 30 is not in valid range (60-86400)).
How preview branches work
When a push arrives on a non-main branch:
Deploy a branch to a preview
- 1
Push to a non-main branch (e.g. feature/login) and Temps checks whether a preview environment already exists for it.
- 2
If none exists, Temps creates one named after the branch (feature/login becomes the feature-login environment).
- 3
Temps builds the application and deploys it to that environment.
- 4
A unique preview URL is generated in the format https://my-app-feature-login.yourdomain.com.
Checkpoint: Open the Environments tab and confirm the branch's preview shows a deployment and a live URL.
- Temps checks if a preview environment already exists for that branch
- If not, it creates one — the environment is named after the branch (e.g.
feature/loginbecomes thefeature-loginenvironment) - The application is built and deployed to that environment
- A unique URL is generated for the preview
If the branch is pushed again, Temps redeploys to the same preview environment rather than creating a new one. If a previously deleted branch is pushed again, Temps restores the soft-deleted environment.
Preview URLs
Preview deployments get URLs in the format:
https://{project-slug}-{environment-slug}.{preview-domain}
For example, if your project is my-app and the branch is feature/login, the preview URL would be:
https://my-app-feature-login.yourdomain.com
Each individual deployment within the preview also gets its own URL:
https://{deployment-slug}.{preview-domain}
These per-deployment URLs let you compare different versions of the same branch.
Control which variables are included
Not all environment variables should be available in preview deployments. For example, you might want to keep production API keys or payment processor credentials out of previews.
Each environment variable has an Include in Preview toggle:
Exclude a variable from previews
- 1
Go to your project's Environment Variables page.
- 2
For each variable, check or uncheck Include in Preview.
- 3
Variables with this toggle off will not be injected into preview environments.
Checkpoint: Confirm production keys like STRIPE_SECRET_KEY show Include in Preview unchecked.
- Go to your project's Environment Variables page
- For each variable, check or uncheck Include in Preview
- Variables with this toggle off will not be injected into preview environments
When creating a new environment variable via the API, set include_in_preview: false to exclude it:
curl -X POST "https://your-temps-instance/api/projects/{project_id}/env-vars" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"key": "STRIPE_SECRET_KEY",
"value": "sk_live_...",
"environment_ids": [1],
"include_in_preview": false
}'
Managed service variables (like POSTGRES_URL and REDIS_URL) are always injected into all environments, including previews. Each preview environment gets its own isolated database within the shared service.
Preview comments on pull requests
When a deployment runs for a branch that has an open GitHub pull request or GitLab merge request, Temps posts a single sticky comment on that PR/MR and updates the same comment in place as the deployment progresses — it never spams a new comment per build. This works identically on GitHub and GitLab.
The comment is keyed by a hidden HTML marker scoped to the project and environment:
<!-- temps-preview:project=N:env=N -->
On each update, Temps finds the existing comment carrying that marker and edits it; if none exists, it creates one. The comment body reflects the current phase of the deployment:
| Phase | Comment heading | Additional content |
|---|---|---|
| Building / in-progress | 🚧 Deploying preview | — |
| Succeeded | ✅ Preview ready | Preview URL |
| Failed | ❌ Preview build failed | Link to deployment logs |
| Cancelled | ⛔ Preview deployment cancelled | Commit SHA + link to deployment logs |
When a deployment is cancelled — from the dashboard or via the API — Temps updates the sticky comment to the cancelled state. Prior to this, cancelling a deployment would leave the comment stuck on 🚧 Deploying preview indefinitely. The cancelled body includes the commit SHA so it is clear which revision was stopped.
Required token scopes
For Temps to post comments, the connection needs write access to pull requests:
| Provider | Scope |
|---|---|
| GitHub App | pull_requests:write |
| GitLab token | api |
Newly created GitHub Apps request pull_requests:write automatically. An existing GitHub App installation that predates this and lacks the scope will hit a 403 — Temps logs it and skips the comment, but you'll need to upgrade the app's permissions to get sticky comments.
Preview comments degrade gracefully. A missing git connection, no open PR/MR, an unsupported provider, or a 401/403 from the provider are all logged and skipped — they never block or fail the deployment itself.
Access preview URLs
Find the URL for any preview deployment:
- Open your project in the dashboard
- Click Environments in the sidebar
- You will see all environments listed — preview environments are marked with a Preview badge
- Click on a preview environment to see its URL and deployment history
Alternatively, on the Deployments page, each deployment entry shows its environment name and a direct link to the live URL.
Create environments manually
Auto-created previews cover feature branches, but you can also create environments by hand for long-lived branches like staging or develop. There is no default cap on the number of environments per project, so you can create as many staging, QA, or long-lived preview environments as your workflow requires:
Create an environment manually
- 1
Go to your project's Environments page.
- 2
Click New Environment.
- 3
Enter a name (e.g. staging).
- 4
Select the branch it tracks (e.g. develop).
- 5
Configure replicas and resource limits (optional).
Checkpoint: Confirm the new environment appears in the Environments list and is never soft-deleted when its branch is removed.
- Go to your project's Environments page
- Click New Environment
- Enter a name (e.g.
staging) - Select the branch it tracks (e.g.
develop) - Configure replicas and resource limits (optional)
Manual environments behave like auto-created previews but are never soft-deleted when the branch is removed.
Per-environment overrides
Each environment can override the project-level deployment configuration:
- Name
branch- Type
- string
- Description
The Git branch this environment tracks.
- Name
replicas- Type
- integer
- Description
Number of container instances. Default: 1.
- Name
exposed_port- Type
- integer
- Description
Override the container port. Default: auto-detected from the image's EXPOSE directive.
- Name
automatic_deploy- Type
- boolean
- Description
Whether pushes to the branch trigger automatic deployments. Default: true.
- Name
cpu_limit- Type
- integer
- Description
CPU limit for containers, in millicores (e.g. 500 for half a core).
- Name
memory_limit- Type
- integer
- Description
Memory limit for containers, in megabytes (e.g. 512).
Notify external systems with webhooks
Temps can fire outbound webhooks when a deployment changes state, so you can post a message to Slack, Discord, or any HTTP endpoint when a preview goes live or fails:
Add a deployment webhook
- 1
Go to Settings > Webhooks.
- 2
Add a webhook URL.
- 3
Select the deployment events you care about (deployment.created, deployment.succeeded, deployment.failed, deployment.cancelled, deployment.ready).
Checkpoint: Trigger a preview deployment and confirm your endpoint receives a payload carrying project_name, environment, branch, commit_sha, status, and url.
- Go to Settings > Webhooks
- Add a webhook URL
- Select the deployment events you care about
The available deployment events are:
| Event | When it fires |
|---|---|
deployment.created | A deployment was queued |
deployment.succeeded | The build completed successfully |
deployment.failed | The deployment failed (build error, health-check timeout, etc.) |
deployment.cancelled | The deployment was cancelled |
deployment.ready | The deployment is live and receiving traffic |
Each deployment payload carries the project_name, environment, branch, commit_sha, commit_message, status, and the live url — enough to build a meaningful notification.
Clean up stale previews
Preview environments accumulate over time as branches are created. To clean them up:
Delete a stale preview environment
- 1
Go to Environments in your project.
- 2
Find the preview environment you want to remove.
- 3
Click the Delete button (or use the actions menu).
Checkpoint: Confirm the environment disappears from the list; its containers are stopped and removed and the record is soft-deleted, but the Git branch is untouched. Production cannot be deleted.
- Go to Environments in your project
- Find the preview environment you want to remove
- Click the Delete button (or use the actions menu)
Deleting a preview environment:
- Stops and removes the running containers
- Soft-deletes the environment record (it can be restored if the same branch is pushed again)
- Does not delete the Git branch
Production protection: The production environment cannot be deleted. This is enforced at the system level regardless of user permissions.
You can also tear down individual deployments without deleting the entire environment.
Tear down a single deployment
- 1
Open the Deployments page.
- 2
Use the Teardown action on any deployment to stop its container and free resources.
Checkpoint: Confirm the deployment's container is stopped while the environment stays available for future pushes.
On the Deployments page, use the Teardown action on any deployment to stop its container and free resources while keeping the environment available for future pushes.
Troubleshooting
Preview not created after a push
- Verify that Preview Environments is toggled on in project settings
- Confirm the push targets a branch other than the project's main branch
- Check your Git provider's webhook delivery logs to confirm Temps received the push
Environments are uncapped by default. A previously common failure mode — previews not being created because the project had reached an environment limit — no longer applies. If a push does not produce a preview, the cause is one of the items above (setting disabled, wrong branch, or a missed webhook), not a cap on environment count.
Preview URL returns 404
- The deployment may still be building — check the Environments tab for its status
- If you serve previews from a custom domain, make sure a wildcard DNS record (for example
*.yourdomain.com) points at your Temps server so every preview subdomain resolves
Preview returned 503 on first load (on-demand environments)
If an on-demand preview returned 503 Service Unavailable the first time you opened it, the container did not become ready within the configured wake timeout.
What to check:
- Open the environment's Runtime Logs in the dashboard. If the container crashed or failed a health check during startup, the log will show the cause (missing env var, port mismatch, out-of-memory kill, etc.).
- Check how long your container takes to start. If it takes longer than the wake timeout, increase the wake timeout in Settings > General (up to 120 seconds). JVM applications, containers that run database migrations on startup, and large images are common culprits.
- Confirm the exposed port matches what the container listens on. Temps probes the port declared in the environment's
exposed_portsetting. If the container listens on a different port, the TCP probe never succeeds and the wake times out. - Retry the request. If the container did start successfully, a single retry immediately after the 503 will land on the now-running container. The 503 indicates the first request was the one that triggered the wake and then timed out waiting; subsequent requests see a running container.
If the preview consistently times out, increase the wake timeout or investigate the container startup logs to reduce cold-start time.
Variables missing in a preview
- Confirm the variable has Include in Preview enabled
- Variables are copied when the preview environment is first created. A variable added afterwards is not retroactively injected into existing previews — add it to the preview environment or recreate the preview