Set Up CI/CD
Temps can deploy on every push to a tracked branch; add CI when you want tests, linting or approval gates to pass first. Turn off auto-deploy for the environment, then have your CI pipeline trigger the deployment through the API or CLI once its checks pass.
How deployment triggers work
Temps receives push webhooks from your Git provider: GitHub, GitLab, Bitbucket Cloud, Gitea or Forgejo, or any HTTPS Git server you point at Temps' webhook URL. See Connect a Git provider to set one up (GitHub, GitLab, Bitbucket, Gitea and Forgejo, other Git servers). When you push to a branch that matches an environment with auto-deploy on, Temps starts a deployment automatically.
The default flow:
Push to main → your Git provider sends a webhook → Temps builds and deploys
With auto-deploy on, CI/CD is already built in for the simple case. You only need external CI when you want to add checks before deployment (tests, linting, type checking) or want programmatic control over when deployments happen.
Branch deletes are silently ignored
When you delete a branch, GitHub and GitLab both send a push webhook with an after SHA of 0000000000000000000000000000000000000000 (40 zeros). Temps detects this null SHA and stops before any pipeline work begins — no build, container, or rollout is created, and nothing appears in your deployment history.
This is intentional: a branch deletion is not a deployable event. If you delete a branch and see no activity in the Temps dashboard for that project, the null-SHA filter is the reason. The webhook was received and acknowledged, but it was discarded before the deployment pipeline started.
Option 1: auto-deploy with CI checks
Keep Temps auto-deploy enabled and run CI checks in parallel. Your CI pipeline runs tests while Temps builds and deploys. If tests fail, you know there is a problem — but the deployment still happens.
This is the simplest approach for solo developers or small teams who want fast feedback:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test
Temps deploys independently — the CI pipeline provides feedback but does not gate the deployment.
Option 2: CI gates before deploy
Disable auto-deploy on the environment and trigger deployments from CI only after checks pass:
Step 1: Disable auto-deploy
Disable auto-deploy on an environment
- 1
Open your project in the dashboard and click Environments in the sidebar.
- 2
Select the environment you want to gate (for example, production), then open its Settings tab.
- 3
Set Automatic Deploy to off.
Checkpoint: Push a commit to the matching branch and confirm Temps logs that auto-deploy is disabled and returns early without creating a build or container.
Go to your project > Environments > select the environment > Settings and set Automatic Deploy to off. To do it from a script, send automatic_deploy: false to the environment settings endpoint:
curl -X PUT "$TEMPS_API_URL/api/projects/$PROJECT_ID/environments/$ENVIRONMENT_ID/settings" \
-H "Authorization: Bearer $TEMPS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"automatic_deploy": false}'
The CLI sets the flag only at project level (bunx @temps-sdk/cli projects config --project <project> --no-auto-deploy). That covers every environment that has no value of its own; an environment set to on keeps deploying.
When Automatic Deploy is off, Temps stops the Git push pipeline before it does any work: a matching webhook from your Git provider is received, but Temps logs that auto-deploy is disabled and returns early — no build, container, or rollout is created. Auto-deploy is opt-in: if neither the project nor the environment has it turned on, push events do not deploy. See How the automatic-deploy flag is resolved below for the exact rules and the two exceptions.
How the automatic-deploy flag is resolved
Automatic Deploy is stored as automatic_deploy on the deployment config, and it can be set at both the project and the environment level. When a Git push webhook arrives, Temps works out the effective value for each matching environment. The environment's own value wins whenever it is set:
| Environment flag | Project flag | Effective auto-deploy |
|---|---|---|
| On | (any) | On |
| Off | (any) | Off (push does not deploy) |
| Not set | On | On (inherited from the project) |
| Not set | Off | Off (inherited from the project) |
| Not set | Not set | Off (push does not deploy) |
So an environment switched off stays off even when the project has auto-deploy on, which is what lets you gate production on CI while staging keeps deploying on push. New environments start with the flag unset, so they follow the project.
In the dashboard, an environment whose flag is unset shows Automatic Deploy as on, and saving its Settings form stores whatever the switch shows. Saving any environment setting therefore turns auto-deploy on explicitly for that environment unless you switch it off first.
This gate runs before any deployment work — before the duplicate-deployment check, before cancelling in-flight deployments, and before queuing the build. When auto-deploy is off, Temps logs an informational message and stops.
Two exceptions bypass the gate and always deploy:
- Manual triggers. The Deploy button and the
trigger-pipelineAPI/CLI are explicit user actions, so they deploy regardless of theautomatic_deploysetting. This is exactly the path used in Step 2 below — disabling auto-deploy never blocks a CI-triggered deploy. - Project creation. Creating a project queues its initial deployment as a user action, so it deploys even when
automatic_deployis off. After that, pushes follow the flag, including the first push to an environment that has never deployed.
The same gate applies to webhooks from every Git provider, because they all go through the same push pipeline.
Step 2: Trigger deployment from CI
Use the Temps API or CLI to trigger a deployment after your CI checks pass:
name: Test and Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- name: Trigger Temps deployment
run: |
curl -X POST "$TEMPS_API_URL/api/projects/$PROJECT_ID/trigger-pipeline" \
-H "Authorization: Bearer $TEMPS_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"environment_id\": $ENVIRONMENT_ID, \"branch\": \"main\"}"
env:
TEMPS_API_URL: ${{ secrets.TEMPS_API_URL }}
TEMPS_API_TOKEN: ${{ secrets.TEMPS_API_TOKEN }}
PROJECT_ID: ${{ secrets.TEMPS_PROJECT_ID }}
ENVIRONMENT_ID: ${{ secrets.TEMPS_ENVIRONMENT_ID }}
Step 3: Store secrets in CI
Create an API token for CI
- 1
Go to Settings > API Keys in the dashboard.
- 2
Create a new token, give it a name like github-actions-ci, and copy the value (it is shown only once).
- 3
In your CI provider, add the secrets TEMPS_API_URL (your instance URL), TEMPS_API_TOKEN (the token you just copied), TEMPS_PROJECT_ID, and TEMPS_ENVIRONMENT_ID.
Checkpoint: Run a CI job that curls $TEMPS_API_URL/api/projects/$PROJECT_ID/trigger-pipeline with the Bearer token and confirm it returns a success response rather than 401 Unauthorized.
Add these secrets to your CI provider:
| Secret | Value | Where to find it |
|---|---|---|
TEMPS_API_URL | Your Temps instance URL (e.g. https://temps.example.com) | Your server configuration |
TEMPS_API_TOKEN | API key from the Temps dashboard | Settings > API Keys |
TEMPS_PROJECT_ID | Numeric project ID | Project URL or API |
TEMPS_ENVIRONMENT_ID | Numeric environment ID | Environment settings |
Deploy via the API
If your CI does the build itself, you don't have to make Temps rebuild from source. Temps exposes endpoints to deploy a pre-built Docker image, upload an image tarball, or push a static bundle. Use these when you want CI to own the build step and Temps to own the rollout.
| Method | When to use | API endpoint |
|---|---|---|
| Pre-built Docker image | You build and push to a registry in CI, then tell Temps to deploy it | POST /api/projects/{id}/environments/{id}/deploy/image |
| Docker image upload | You build an image in CI and upload the tarball directly (no registry needed) | POST /api/projects/{id}/environments/{id}/deploy/image-upload |
| Static bundle | You build static files in CI and upload a zip or tar.gz | POST /api/projects/{id}/upload/static then .../deploy/static |
| Git trigger | You want Temps to build from source, but triggered by CI instead of a webhook | POST /api/projects/{id}/trigger-pipeline |
All endpoints require an API token in the Authorization: Bearer header.
Deploy a pre-built Docker image
If your CI pushes an image to a registry (Docker Hub, GitHub Container Registry, etc.), tell Temps to pull and deploy it:
curl -X POST "$TEMPS_API_URL/api/projects/$PROJECT_ID/environments/$ENVIRONMENT_ID/deploy/image" \
-H "Authorization: Bearer $TEMPS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"image_ref": "ghcr.io/your-org/your-app:sha-abc1234"}'
The body field is image_ref (or external_image_id for an image you registered earlier). Image deploys can't read .temps.yaml, so pass "health_check_path": "/api/healthz" if your health endpoint isn't /.
Temps pulls the image, starts a container, runs health checks, and shifts traffic to it. The old container is stopped once the new one is healthy.
Upload a Docker image without a registry
If you don't want to push to a registry, upload the image tarball directly (up to 1 GB):
# Save the image as a tarball in your CI
docker save your-app:latest | gzip > image.tar.gz
# Upload and deploy it
curl -X POST "$TEMPS_API_URL/api/projects/$PROJECT_ID/environments/$ENVIRONMENT_ID/deploy/image-upload" \
-H "Authorization: Bearer $TEMPS_API_TOKEN" \
-F "image=@image.tar.gz"
Deploy a static bundle
For static sites (React, Vue, Astro, etc.) built in CI, upload the build output (tar.gz or zip, up to 500 MB), then deploy it:
# Build in CI, then archive the output
npm run build
tar -czf build.tar.gz -C dist .
# Step 1: upload the bundle and keep the bundle id from the response
BUNDLE_ID=$(curl -fsS -X POST "$TEMPS_API_URL/api/projects/$PROJECT_ID/upload/static" \
-H "Authorization: Bearer $TEMPS_API_TOKEN" \
-F "file=@build.tar.gz" | jq -r '.id')
# Step 2: deploy that bundle
curl -X POST "$TEMPS_API_URL/api/projects/$PROJECT_ID/environments/$ENVIRONMENT_ID/deploy/static" \
-H "Authorization: Bearer $TEMPS_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"static_bundle_id\": $BUNDLE_ID}"
The deploy call takes static_bundle_id (the id field of the upload response), not the whole upload response.
Using the Temps CLI
The Temps CLI provides more control than raw API calls. Install it in your CI environment:
# Install
npx @temps-sdk/cli --help
# Or with bun
bunx @temps-sdk/cli --help
Deploy from Git
Deploy from Git
- 1
Install the Temps CLI in your CI environment with bunx @temps-sdk/cli --help.
- 2
Export TEMPS_API_URL and TEMPS_API_TOKEN so the CLI is authenticated.
- 3
Run the deploy command after your CI checks pass, targeting the project, environment, and branch/commit you want.
Checkpoint: The command waits for the deployment by default and exits 0 only once it completes; confirm the new deployment shows as ready in Deployments.
Trigger a deployment for a specific branch or commit:
# Deploy the current branch
bunx @temps-sdk/cli deploy --project my-app --environment production
# Deploy a specific commit
bunx @temps-sdk/cli deploy --project my-app --environment production --commit abc123
# Return immediately instead of waiting for deployment to complete
bunx @temps-sdk/cli deploy --project my-app --environment production --no-wait
Deploy a pre-built image
Deploy a pre-built image
- 1
Build and push your image to a registry (Docker Hub, GitHub Container Registry, etc.) in CI.
- 2
Export TEMPS_API_URL and TEMPS_API_TOKEN for the CLI.
- 3
Run the deploy:image command pointing at the pushed image tag.
Checkpoint: Temps pulls the image, runs health checks, and shifts traffic; confirm the deployment reaches the ready state before the old container is stopped.
If your CI builds a Docker image, deploy it directly without rebuilding on Temps:
bunx @temps-sdk/cli deploy:image \
--image ghcr.io/your-org/your-app:latest \
--project my-app \
--environment production \
--timeout 300
Deploy static files
Deploy static files
- 1
Run your static build in CI so the output (for example ./dist) is ready.
- 2
Export TEMPS_API_URL and TEMPS_API_TOKEN for the CLI.
- 3
Run the deploy:static command pointing --path at your build output directory or archive.
Checkpoint: Confirm a new static deployment appears in Deployments and is serving the uploaded bundle.
Upload static files from your CI build output:
bunx @temps-sdk/cli deploy:static \
--path ./dist \
--project my-app \
--environment production
Authentication
Set these environment variables for the CLI:
export TEMPS_API_URL=https://temps.example.com
export TEMPS_API_TOKEN=your-api-token
Outbound webhooks
Temps can send deployment event notifications to external systems. Use this to post to Slack, update a status page, or trigger downstream actions. This section covers the CI-relevant part; see Webhooks for every event, the payloads and signature verification.
Setting up
Add an outbound webhook
- 1
Go to Settings > Webhooks in your project.
- 2
Add a webhook URL (for example, a Slack incoming webhook URL).
- 3
Select the events to listen for (such as deployment.succeeded and deployment.failed).
- 4
Optionally set a secret for HMAC signature verification, then save.
Checkpoint: Trigger a deployment and check the webhook delivery history; confirm a delivery was sent and any failed delivery can be retried.
- Go to Settings > Webhooks in your project
- Add a webhook URL (e.g. a Slack incoming webhook URL)
- Select events to listen for
- Optionally set a secret for HMAC signature verification
Events for CI pipelines
| Event | Description |
|---|---|
deployment.succeeded | The deployment completed successfully |
deployment.failed | The deployment failed |
deployment.ready | The deployment is live and receiving traffic |
Deployment payloads carry the project name and ID, environment, branch, commit SHA and message, status and live URL. Deliveries are signed with HMAC-SHA256 when you set a secret. Failed deliveries are not retried automatically; retry them from the delivery history in the dashboard, the API or the CLI. The other two delivered events (deployment.created and deployment.cancelled) and the signature format are in Webhooks; project, domain and email events can be selected but are not emitted in the current release.
Rollback
Failed deploys never take traffic
There is no automatic rollback in Temps, and a failed deploy doesn't need one: before cutover, Temps health-checks the new containers, and if they fail the check the deployment is marked failed while the current deployment keeps serving. Traffic was never switched. Once a deployment passes the check and goes live, nothing reverts it automatically, so a deploy that is healthy but buggy needs a manual rollback. See Rollbacks for how rollbacks work and which deployments you can roll back to.
Manual rollback
Roll back to a previous deployment
- 1
Go to your project > Deployments.
- 2
Find the last working deployment.
- 3
Open its menu and choose Rollback to this.
Checkpoint: Confirm a new deployment shows the Current badge and your app serves the working version again.
- Go to your project > Deployments
- Find the last working deployment
- Open its menu and choose Rollback to this
Temps creates a new deployment from that version, reusing its image or rebuilding its commit. From CI, pass the target deployment ID with --to; earlier deployments that a newer one replaced (status stopped) are valid targets.
Git-based rollback
Revert the commit and push:
git revert HEAD
git push origin main
Temps deploys the reverted commit as a new deployment.
Complete example: Next.js with GitHub Actions
.github/workflows/deploy.yml
name: Test and Deploy
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run lint
- run: npx tsc --noEmit
- run: npm test -- --ci
deploy:
needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: Deploy to Temps
run: |
curl -X POST "$TEMPS_API_URL/api/projects/$PROJECT_ID/trigger-pipeline" \
-H "Authorization: Bearer $TEMPS_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"environment_id\": $ENVIRONMENT_ID, \"branch\": \"main\", \"commit\": \"${{ github.sha }}\"}"
env:
TEMPS_API_URL: ${{ secrets.TEMPS_API_URL }}
TEMPS_API_TOKEN: ${{ secrets.TEMPS_API_TOKEN }}
PROJECT_ID: ${{ secrets.TEMPS_PROJECT_ID }}
ENVIRONMENT_ID: ${{ secrets.TEMPS_ENVIRONMENT_ID }}