Skip to content

GitHub Actions deployment

Add this person to Vercel so they can deploy. Or just don’t.

How a historical GitHub Actions workflow handled shared staging and authorized production releases without purchasing a Vercel seat for every engineer.

5 min readBy

Dashboard access is not the same as deployment access

At the time of this implementation, Vercel Pro cost $20 per seat. Three engineers would cost $60 each month and five or six would cost $100 to $120. The team kept one $20 project seat and moved deployment execution into GitHub Actions through the Vercel CLI.

The repository stored VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_PROJECT_ID as secrets. Engineers could ship reviewed work through the normal trunk-based merge path without each receiving dashboard credentials.

Separate staging from production

I split deployment into two workflows. Merges to main updated staging; production required a manual release after QA from an authorized GitHub actor.

Pull request -> review -> merge to main
  -> automatic staging build and deploy
  -> QA
  -> authorized production workflow
  -> tag, release, deploy, Slack notice

Repository access did not automatically grant production access. The original workflow allowed Elusive7733 to trigger production while the rest of the team shipped through pull requests and staging.

Automate the environment used for review

Every push to main pulled the staging environment, built a prebuilt artifact with --target=staging, and deployed that exact output. A shared concurrency group used cancel-in-progress: true so a newer main commit replaced an obsolete queued or running staging deploy.

concurrency:
  group: staging-deploy
  cancel-in-progress: true

- run: vercel pull --yes --environment=staging --token=$VERCEL_TOKEN
- run: vercel build --target=staging --token=$VERCEL_TOKEN
- run: vercel deploy --prebuilt --target=staging --token=$VERCEL_TOKEN

Keep production deliberate

The manual production workflow accepted patch, minor, or major. With full Git history available, it read the latest tag, calculated the next semantic version, created and pushed the tag, pulled the production environment, built the prebuilt artifact, and deployed it.

A separate skip-tagging path reused the latest version for a redeploy. Recovery did not need to create a false new release number merely to rerun infrastructure.

on:
  workflow_dispatch:
    inputs:
      version_type:
        type: choice
        options: [patch, minor, major]
      skip_tagging:
        type: boolean
        default: false

Leave an operational trail

After a tagged release, gh release create --generate-notes produced the changelog. .github/release.yml grouped pull requests by label. The workflow appended deployment metadata and sent a formatted release through a Slack webhook.

I built an AI changelog generator first and deleted it three days later. GitHub already listed every merged pull request and link, so the model added code without adding useful release information.

Spend on the permission the system actually needs

The working sequence was simple: merge a reviewed pull request, deploy staging, verify it, then let an authorized actor trigger production. Version tags, GitHub releases, and Slack messages left a release trail outside the Vercel dashboard.

  • Use repository and environment protection for the credentials that replace dashboard access.
  • Separate automatic review deployments from deliberate production execution.
  • Build once and deploy the prebuilt artifact for each environment.
  • Treat native release notes as the default before adding another generation system.