> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are the docs for Sevalla, a platform for deploying applications, databases, static sites, object storage and load balancers. The dashboard is at https://app.sevalla.com.
> Bold text in steps is an exact dashboard label.
> To act on a Sevalla account, prefer the Sevalla MCP server (https://mcp.sevalla.com/mcp, see https://docs.sevalla.com/ai-agents/overview.md), then the Sevalla CLI (https://docs.sevalla.com/developer-tools/cli.md), then the REST API (https://api-docs.sevalla.com, OpenAPI spec at https://api.sevalla.com/v3/openapi.json).
> Every page index is at https://docs.sevalla.com/llms.txt.

# GitHub Actions

> Deploy Sevalla applications and static sites and promote pipeline apps from GitHub Actions with sevalla-hosting/sevalla-deploy, a deploy hook or the Sevalla CLI.

The Sevalla GitHub Action, [`sevalla-hosting/sevalla-deploy`](https://github.com/sevalla-hosting/sevalla-deploy), starts a deployment of an application or static site, or a promotion in a [pipeline](/pipelines/overview), from a GitHub Actions workflow, and can wait until it finishes. Use it when a deployment should happen only after your workflow's tests or image build pass. If every push to a branch should deploy, [automatic deployments](/applications/deployments) need no workflow at all.

The current version is `v2.0.0`, which uses the [Sevalla API](/developer-tools/api) v3. Versions `v1.0.0` and `v1.0.1` call an older API version and don't take `pipeline-id`; move to `v2.0.0`.

## Before you begin

* An [API key](/developer-tools/api-keys#create-an-api-key) for the workflow, with custom capabilities limited to the service it deploys:

  | Action | Permissions |
  | - | - |
  | `deploy-app` | `APP:UPDATE`, and `APP:READ` to wait for the result |
  | `deploy-static-site` | `STATIC_SITE:UPDATE`, and `STATIC_SITE:READ` to wait for the result |
  | `promote-app` | `PIPELINE:UPDATE`, and `APP:READ` on the target applications to wait for the result |

* The key stored as a repository secret: in GitHub, open the repository's **Settings** > **Secrets and variables** > **Actions**, and add it as `SEVALLA_TOKEN`.

* The service's ID: in the Sevalla dashboard, open the application or static site, click the **Settings** tab, and copy **ID** under **Details**.

* If the action deploys the same branch Sevalla already deploys on every push, turn off automatic deployments for it, or each push deploys twice.

## Deploy an application after tests pass

```yaml .github/workflows/deploy.yml theme={null}
name: Deploy storefront

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: sevalla-hosting/sevalla-deploy@v2.0.0
        with:
          action: deploy-app
          sevalla-token: ${{ secrets.SEVALLA_TOKEN }}
          app-id: 7f3c2b1a-9e8d-4c7b-a6f5-4e3d2c1b0a9f
          branch: main
          wait-for-finish: true
```

The `deploy` job starts once `test` passes. In the job's log, the action prints `Deployment (id: ...) triggered successfully!`, then `Deployment status: ...` every 5 seconds. The job succeeds when the deployment's status is `success`, and the deployment appears on the application's **Deployments** tab.

## Inputs

| Input | Used by | What it is | Default |
| - | - | - | - |
| `action` | All | `deploy-app`, `deploy-static-site` or `promote-app`. Required. | None |
| `sevalla-token` | All | The API key, from a secret. Not needed for `deploy-hook-url` without `wait-for-finish`. | None |
| `app-id` | `deploy-app` | The application's ID | None |
| `static-site-id` | `deploy-static-site` | The static site's ID | None |
| `pipeline-id` | `promote-app` | The pipeline's ID | None |
| `source-app-id` | `promote-app` | The application to promote from | None |
| `target-app-ids` | `promote-app` | The applications to promote to, separated by commas | None |
| `branch` | `deploy-app`, `deploy-static-site` | The branch to deploy. Always set it for `deploy-static-site`. | The application's default branch (`deploy-app`) |
| `docker-image` | `deploy-app` | The image to deploy, for an application that runs a Docker image | The application's image |
| `is-restart` | `deploy-app` | `true` redeploys the current build without building again | `false` |
| `wait-for-finish` | All | `true` polls the deployment every 5 seconds until it ends, and fails the step unless it ends with `success` | `false` |
| `deploy-hook-url` | `deploy-app` | A [deploy hook](/applications/deployments#deploy-with-a-deploy-hook) URL to call instead of the API | None |

## Outputs

| Output | Set by | Value |
| - | - | - |
| `deployment-id` | `deploy-app`, `deploy-static-site` | The ID of the deployment the action started |
| `deployment-ids` | `promote-app` | The IDs of the deployments the promotion started, one per target application |

Give the step an `id` to read an output in a later step, for example `${{ steps.deploy.outputs.deployment-id }}`.

## Wait for the result

With `wait-for-finish: true`, the action checks the deployment every 5 seconds until its status is `success`, `failed`, `cancelled` or `skipped`, and fails the step with `Deployment failed: status is ...` for anything but `success`. It stops after 1,000 checks (about 83 minutes) with `Max retries reached while polling deployment status`. A check that can't read the status is logged and retried. Without `wait-for-finish`, the step succeeds as soon as Sevalla accepts the deployment.

## Deploy a Docker image

Build and push an image in the same workflow, then deploy that exact tag:

```yaml .github/workflows/deploy.yml theme={null}
      - uses: sevalla-hosting/sevalla-deploy@v2.0.0
        with:
          action: deploy-app
          sevalla-token: ${{ secrets.SEVALLA_TOKEN }}
          app-id: 7f3c2b1a-9e8d-4c7b-a6f5-4e3d2c1b0a9f
          docker-image: ghcr.io/acme/storefront:${{ github.sha }}
          wait-for-finish: true
```

The application must run a [Docker image](/applications/docker-images), with a registry credential if the image is private.

## Deploy a static site

```yaml .github/workflows/deploy-site.yml theme={null}
      - uses: sevalla-hosting/sevalla-deploy@v2.0.0
        with:
          action: deploy-static-site
          sevalla-token: ${{ secrets.SEVALLA_TOKEN }}
          static-site-id: 3b2a1c0d-8e7f-4a6b-9c5d-2e1f0a9b8c7d
          branch: main
          wait-for-finish: true
```

## Promote in a pipeline

```yaml .github/workflows/promote.yml theme={null}
      - uses: sevalla-hosting/sevalla-deploy@v2.0.0
        with:
          action: promote-app
          sevalla-token: ${{ secrets.SEVALLA_TOKEN }}
          pipeline-id: 5e4d3c2b-1a0f-4e9d-8c7b-6a5f4e3d2c1b
          source-app-id: 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
          target-app-ids: 7f3c2b1a-9e8d-4c7b-a6f5-4e3d2c1b0a9f
          wait-for-finish: true
```

The pipeline's ID is the last part of the pipeline's URL in the dashboard (`/pipeline/<id>`), and `sevalla pipelines list` shows it too. With `wait-for-finish`, the action waits for every target application's deployment.

## Deploy with a deploy hook

A [deploy hook](/applications/deployments#deploy-with-a-deploy-hook) is a secret URL that deploys an application when it receives a `POST`, with no API key: the latest commit of its default branch, or its current Docker image. Store the URL as a secret, such as `SEVALLA_DEPLOY_HOOK_URL`, and either pass it to the action:

```yaml theme={null}
      - uses: sevalla-hosting/sevalla-deploy@v2.0.0
        with:
          action: deploy-app
          deploy-hook-url: ${{ secrets.SEVALLA_DEPLOY_HOOK_URL }}
```

or call it from any CI system:

```bash theme={null}
curl -fsS -X POST "$SEVALLA_DEPLOY_HOOK_URL"
```

The hook answers with `{"message":"App is being deployed","deployment":{"id":"..."}}`. To wait for the result with the action, also set `sevalla-token`, `app-id` and `wait-for-finish: true`; without `app-id`, the action skips waiting with a warning. A deploy hook can't pick a branch or an image: use the API key inputs for that.

## Use the Sevalla CLI in a workflow

For steps the action doesn't cover, install the [Sevalla CLI](/developer-tools/cli) and give it the key in `SEVALLA_API_TOKEN`:

```yaml .github/workflows/deploy.yml theme={null}
      - uses: actions/setup-node@v4
        with:
          node-version: 24
      - run: npm install -g @sevalla/cli
      - run: sevalla apps deployments trigger 7f3c2b1a-9e8d-4c7b-a6f5-4e3d2c1b0a9f --branch main --json
        env:
          SEVALLA_API_TOKEN: ${{ secrets.SEVALLA_TOKEN }}
```

The CLI prints JSON in CI. To wait for the deployment, poll `sevalla apps deployments get` as shown in [Use JSON output in scripts](/developer-tools/cli#use-json-output-in-scripts).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.