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. 1

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

  2. 2

    Select the environment you want to gate (for example, production), then open its Settings tab.

  3. 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 flagProject flagEffective auto-deploy
On(any)On
Off(any)Off (push does not deploy)
Not setOnOn (inherited from the project)
Not setOffOff (inherited from the project)
Not setNot setOff (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:

  1. Manual triggers. The Deploy button and the trigger-pipeline API/CLI are explicit user actions, so they deploy regardless of the automatic_deploy setting. This is exactly the path used in Step 2 below — disabling auto-deploy never blocks a CI-triggered deploy.
  2. Project creation. Creating a project queues its initial deployment as a user action, so it deploys even when automatic_deploy is 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. 1

    Go to Settings > API Keys in the dashboard.

  2. 2

    Create a new token, give it a name like github-actions-ci, and copy the value (it is shown only once).

  3. 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:

SecretValueWhere to find it
TEMPS_API_URLYour Temps instance URL (e.g. https://temps.example.com)Your server configuration
TEMPS_API_TOKENAPI key from the Temps dashboardSettings > API Keys
TEMPS_PROJECT_IDNumeric project IDProject URL or API
TEMPS_ENVIRONMENT_IDNumeric environment IDEnvironment 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.

MethodWhen to useAPI endpoint
Pre-built Docker imageYou build and push to a registry in CI, then tell Temps to deploy itPOST /api/projects/{id}/environments/{id}/deploy/image
Docker image uploadYou build an image in CI and upload the tarball directly (no registry needed)POST /api/projects/{id}/environments/{id}/deploy/image-upload
Static bundleYou build static files in CI and upload a zip or tar.gzPOST /api/projects/{id}/upload/static then .../deploy/static
Git triggerYou want Temps to build from source, but triggered by CI instead of a webhookPOST /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. 1

    Install the Temps CLI in your CI environment with bunx @temps-sdk/cli --help.

  2. 2

    Export TEMPS_API_URL and TEMPS_API_TOKEN so the CLI is authenticated.

  3. 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. 1

    Build and push your image to a registry (Docker Hub, GitHub Container Registry, etc.) in CI.

  2. 2

    Export TEMPS_API_URL and TEMPS_API_TOKEN for the CLI.

  3. 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. 1

    Run your static build in CI so the output (for example ./dist) is ready.

  2. 2

    Export TEMPS_API_URL and TEMPS_API_TOKEN for the CLI.

  3. 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. 1

    Go to Settings > Webhooks in your project.

  2. 2

    Add a webhook URL (for example, a Slack incoming webhook URL).

  3. 3

    Select the events to listen for (such as deployment.succeeded and deployment.failed).

  4. 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.

  1. Go to Settings > Webhooks in your project
  2. Add a webhook URL (e.g. a Slack incoming webhook URL)
  3. Select events to listen for
  4. Optionally set a secret for HMAC signature verification

Events for CI pipelines

EventDescription
deployment.succeededThe deployment completed successfully
deployment.failedThe deployment failed
deployment.readyThe 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. 1

    Go to your project > Deployments.

  2. 2

    Find the last working deployment.

  3. 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.

  1. Go to your project > Deployments
  2. Find the last working deployment
  3. 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 }}

What to explore next

Preview deployments Environment variables Webhooks Rollbacks Scale your application Add monitoring

Last updated

Was this page helpful?