Back to Blog
Tutorial12 min read2026-08-01

Scripting Astro Deployments in CI with the PandaStack API

Automate Astro documentation site deploys from GitHub Actions. Use the REST API with a psk_ token to trigger builds on every push, with zero manual clicks.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

A documentation site built with Astro should deploy automatically when you push to main. Manually clicking Deploy in a dashboard slows down the release cycle and breaks the Git-centric workflow. GitHub Actions can trigger a PandaStack deploy via the REST API, and the entire process — from commit to live site — takes under two minutes.

The key piece is a psk_ token, which authenticates API requests without needing a user session. PandaStack generates one per organization, and it stays valid until you revoke it. Store it as a GitHub secret, and your workflow can deploy on every push.

Create a PandaStack API token

Log in to the dashboard, go to Settings → API Tokens, and generate a new token. Copy the value — it looks like psk_live_abc123xyz — and never commit it to Git.

Add it as a GitHub repository secret:

  1. 1Go to your repo → Settings → Secrets and variables → Actions
  2. 2Click New repository secret
  3. 3Name: PANDASTACK_TOKEN
  4. 4Value: psk_live_abc123xyz

Now GitHub Actions can reference ${{ secrets.PANDASTACK_TOKEN }} in workflows.

Set up the Astro project

Your Astro config should define the output directory (usually dist):

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  outDir: 'dist'
});

Commit a pandastack.json to declare the build configuration:

{
  "type": "static",
  "language": "nodejs",
  "buildCommand": "npm run build",
  "outputDir": "dist"
}

This file isn't required for the API deploy, but it helps if someone uses the deploy button or CLI later.

Create the project on PandaStack

Use the API to register the Astro site as a project:

curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer $PANDASTACK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "static",
    "name": "astro-docs",
    "repositoryName": "acme/astro-docs",
    "branch": "main",
    "autoDeploy": false
  }'

Set autoDeploy: false because GitHub Actions will trigger deploys explicitly. The response includes a projectId — save it for the next step.

Write the GitHub Actions workflow

Create .github/workflows/deploy.yml:

name: Deploy to PandaStack

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger PandaStack deploy
        run: |
          curl -X POST https://api.pandastack.io/v1/projects/<project-id>/deploy \
            -H "Authorization: Bearer ${{ secrets.PANDASTACK_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{"branch": "main"}'

Replace with the projectId from the previous step. Commit and push this file to main.

What happens on push

Every time you push to main:

  1. 1GitHub Actions runs the workflow
  2. 2The workflow POSTs to /v1/projects//deploy
  3. 3PandaStack clones the repository, runs npm install and npm run build, uploads dist/ to a CDN
  4. 4The deploy finishes in 60-90 seconds
  5. 5The site is live at https://astro-docs.pandastack.app

Check the GitHub Actions log to see the API response. It includes a deploymentId you can use to fetch build logs.

Stream build logs in the workflow

The deploy endpoint returns a deploymentUuid. Use it to poll the build status:

- name: Trigger deploy
  id: deploy
  run: |
    response=$(curl -s -X POST https://api.pandastack.io/v1/projects/<project-id>/deploy \
      -H "Authorization: Bearer ${{ secrets.PANDASTACK_TOKEN }}" \
      -H "Content-Type: application/json" \
      -d '{"branch": "main"}')
    echo "response=$response" >> $GITHUB_OUTPUT
    deploymentUuid=$(echo $response | jq -r '.data.deploymentUuid')
    echo "deploymentUuid=$deploymentUuid" >> $GITHUB_OUTPUT

- name: Wait for build
  run: |
    deploymentUuid=${{ steps.deploy.outputs.deploymentUuid }}
    while true; do
      status=$(curl -s https://api.pandastack.io/v1/projects/<project-id>/deployments/$deploymentUuid \
        -H "Authorization: Bearer ${{ secrets.PANDASTACK_TOKEN }}" \
        | jq -r '.data.status')
      echo "Deploy status: $status"
      if [ "$status" = "RUNNING" ]; then
        exit 0
      elif [ "$status" = "FAILED" ]; then
        exit 1
      fi
      sleep 10
    done

This polls the deployment status every 10 seconds until it succeeds or fails. If the build fails, the GitHub Actions job fails too, and you get a notification.

Deploy preview environments for pull requests

Extend the workflow to deploy every pull request as a separate preview:

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Set branch and project name
        id: vars
        run: |
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            echo "branch=${{ github.head_ref }}" >> $GITHUB_OUTPUT
            echo "project_name=astro-docs-pr-${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
          else
            echo "branch=main" >> $GITHUB_OUTPUT
            echo "project_name=astro-docs" >> $GITHUB_OUTPUT
          fi

      - name: Create or deploy project
        run: |
          curl -X POST https://api.pandastack.io/v1/projects \
            -H "Authorization: Bearer ${{ secrets.PANDASTACK_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{
              "slug": "static",
              "name": "${{ steps.vars.outputs.project_name }}",
              "repositoryName": "acme/astro-docs",
              "branch": "${{ steps.vars.outputs.branch }}",
              "autoDeploy": false
            }' || true

          curl -X POST https://api.pandastack.io/v1/projects/${{ steps.vars.outputs.project_name }}/deploy \
            -H "Authorization: Bearer ${{ secrets.PANDASTACK_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{"branch": "${{ steps.vars.outputs.branch }}"}'

Now every pull request gets its own deploy at https://astro-docs-pr-42.pandastack.app. Reviewers can test changes before merging.

Clean up preview deploys

Add a workflow that deletes the preview project when the pull request closes:

name: Cleanup preview

on:
  pull_request:
    types: [closed]

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Delete preview project
        run: |
          curl -X DELETE https://api.pandastack.io/v1/projects/astro-docs-pr-${{ github.event.pull_request.number }} \
            -H "Authorization: Bearer ${{ secrets.PANDASTACK_TOKEN }}"

This keeps your dashboard clean and avoids paying for idle previews.

Use the CLI instead of curl

If you prefer the CLI, add a step that installs it:

- name: Install PandaStack CLI
  run: |
    curl -fsSL https://cli.pandastack.io/install.sh | sh
    echo "$HOME/.pandastack/bin" >> $GITHUB_PATH

- name: Deploy
  env:
    PANDASTACK_TOKEN: ${{ secrets.PANDASTACK_TOKEN }}
  run: |
    panda login --token $PANDASTACK_TOKEN
    panda projects deploy astro-docs

The CLI handles authentication and polling automatically, so the workflow is shorter. The trade-off is an extra dependency.

Debugging deploy failures

401 Unauthorized: The PANDASTACK_TOKEN secret is missing or invalid. Check Settings → Secrets in GitHub.

404 Project not found: The project name or ID is wrong. Verify it with curl https://api.pandastack.io/v1/projects -H "Authorization: Bearer $PANDASTACK_TOKEN".

Build succeeds but site doesn't update: Check that the outputDir in pandastack.json matches the actual build output. Astro uses dist by default, but some configurations change it to build or public.

GitHub Actions timeout: The deploy took longer than the workflow timeout (default 1 hour). Increase it with timeout-minutes: 120 in the job definition.

Compare to webhook-based deploys

An alternative to the API workflow is enabling autoDeploy: true on the project. PandaStack listens for GitHub push events and deploys automatically. The downside is less control: you can't run tests before deploying, and preview environments are harder to set up.

The API workflow gives you full control over when and how deploys happen, which is better for production workflows.

References

  • [PandaStack API documentation](https://docs.pandastack.io/api)
  • [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
  • [Astro build configuration](https://docs.astro.build/en/reference/configuration-reference/)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also