Skip to content

Part 7: CI/CD

In this last part you’ll deploy both sites from GitHub Actions on every push to main, reaching each console through ui.com.

CI has no Alchemy profiles, so it uses environment variables instead. In your repository’s Settings → Environments, create home and office, each with these secrets:

Secret Value
UNIFI_CONSOLE_ID The console’s ID: run npx alchemy profile show with its profile
UNIFI_API_KEY A Site Manager API key from unifi.ui.com
LAB_WIFI_PASSPHRASE The lab’s Wi-Fi passphrase

Create .github/workflows/deploy.yml:

.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
strategy:
matrix:
site: [home, office]
runs-on: ubuntu-latest
environment: ${{ matrix.site }}
concurrency: deploy-${{ matrix.site }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
- run: npm ci
- run: npx alchemy deploy --stage ${{ matrix.site }} --yes
env:
UNIFI_CONSOLE_ID: ${{ secrets.UNIFI_CONSOLE_ID }}
UNIFI_API_KEY: ${{ secrets.UNIFI_API_KEY }}
LAB_WIFI_PASSPHRASE: ${{ secrets.LAB_WIFI_PASSPHRASE }}

Each site deploys in its own job, with its own environment’s secrets. concurrency queues deploys of one site, since the state store locks each stage while it runs.

Commit and push to main. The workflow deploys both sites, and the job logs show each plan.

When you’re done with the lab, destroy each site’s Stack:

Terminal window
npx alchemy destroy --stage home
npx alchemy destroy --stage office --profile office

The lab network, Wi-Fi, zone, policies, list and DNS records are deleted. The default networks stay, because they’re retained.