Skip to content

Deploy to your own server from GitHub Actions ​

By the end of this tutorial every push to main builds your image, deploys it to your own server, and turns the workflow red if the rollout fails. Levelrail's deploy action does the last step; you keep your existing build step.

Before you start ​

  • A running Levelrail instance with an existing app to deploy to (Deploy a Docker app creates one).
  • A container registry the server can pull from. This example uses GitHub's ghcr.io.
  • Permission to add secrets to your GitHub repository.

1. Create a deploy-only token ​

CI should not hold your admin credentials. Mint a token that can read and deploy, and nothing else:

bash
levelrail-cli tokens create --name github-actions --preset deployer --expires-in-days 90

The command asks for your admin username and password, because a token can only be created from a live session. The token prints once and cannot be shown again. deployer means the read and deploy abilities. See Identity and access for the full list.

2. Store the URL and token as secrets ​

bash
gh secret set LEVELRAIL_API_URL --body "https://console.example.com"
gh secret set LEVELRAIL_API_TOKEN

The second command prompts for the value, so the token never lands in your shell history.

3. Let the server pull your image ​

If the image is public, skip this. For a private ghcr.io package, give Levelrail pull credentials once. See Registry credentials, and test them with levelrail-cli registry-credentials test <id> before the first deploy.

4. Add the workflow ​

Create .github/workflows/deploy.yml:

yaml
name: Deploy

on:
  push:
    branches: [main]

permissions:
  contents: read
  packages: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/build-push-action@v6
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

      - name: Deploy to Levelrail
        uses: glincker/levelrail/.github/actions/deploy@main
        with:
          api-url: ${{ secrets.LEVELRAIL_API_URL }}
          api-token: ${{ secrets.LEVELRAIL_API_TOKEN }}
          app: my-app
          image: ghcr.io/${{ github.repository }}:${{ github.sha }}
          cli-version: v0.2.0-beta.15

The action installs the CLI (with its checksum verified), runs apps deploy, then runs apps wait and fails the job if the rollout fails or times out. Without cli-version it picks the newest release. Pin it, as above, if you want builds that never change underneath you.

Tag the image with the commit SHA, not latest. A SHA tag is immutable, so the deploy history tells you exactly which commit each release is, and rollback has a precise target.

5. Push and watch ​

Push to main. The workflow's last step prints:

text
waiting for "my-app" to converge... (succeeded)
"my-app" rolled out

and the deploy appears in levelrail-cli apps deploys list my-app and on the dashboard's Deployments page.

Useful inputs ​

InputDefaultWhat it does
waittrueSet false to return as soon as the deploy is accepted.
timeout5mHow long to wait for the rollout to converge.
confirmfalseRequired to deploy into a protected environment.

The action's status output is succeeded, failed, or skipped.

Not on GitHub? ​

The action is a thin wrapper over two commands, so any CI works. Install the CLI with the install script, export APP_API_URL and APP_API_TOKEN, then run levelrail-cli apps deploy my-app --image "$IMAGE" and levelrail-cli apps wait my-app. Both exit non-zero on failure.

Where to go next ​

Released under the Apache 2.0 License.