7.1 KiB
| project | type | status | path | tags | created | updated | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| pbsii-cicd-pipeline | project-plan | active | Tech/Projects |
|
2026-04-18 | 2026-04-18 |
PBSII CI/CD Pipeline
Goal
Automate deployment of PBSII layers using GitHub Actions to trigger Ansible playbooks. Each layer deploys independently based on which files changed. Start with the dashboard layer as the first pipeline, then extend to collector and parser.
Context
- PBSII is a monorepo on GitHub (private) with subdirectories:
collector/,parser/,dashboard/,n8n/ - Ansible already handles all server config (tagged) and Docker deployments (migrating to per-container tags)
- A standalone pbs-hub deploy role exists as a reference pattern
- Each layer deploys differently: dashboard is Docker, collector is systemd, parser is Python/venv
- n8n workflows are just version-controlled JSON — no CI/CD needed
Architecture
GitHub Push (to dashboard/)
→ GitHub Actions workflow triggers
→ Checks out PBSII repo
→ Installs Ansible on runner
→ Runs Ansible playbook with dashboard tag
→ Ansible SSHs to Linode, deploys dashboard container
One workflow file per layer. Explicit and simple — no magic detection.
Phase 1: SSH Deploy Key Setup
1a. Generate deploy key (local machine)
ssh-keygen -t ed25519 -C "github-actions-deploy" -f
~/.ssh/github_deploy_linode
- No passphrase (key lives encrypted in GitHub Secrets)
- This key is dedicated to GitHub Actions — not your personal key
1b. Install public key on Linode
ssh-copy-id -i ~/.ssh/github_deploy_linode.pub your_user@SERVER_IP
- Verify:
ssh -i ~/.ssh/github_deploy_linode your_user@SERVER_IPconnects without password prompt
1c. Add GitHub Secrets
In the PBSII GitHub repo → Settings → Secrets and variables → Actions:
DEPLOY_SSH_KEY— contents of~/.ssh/github_deploy_linode(the private key)DEPLOY_HOST— your Linode server IPDEPLOY_USER— your SSH username on LinodeANSIBLE_VAULT_PASSWORD— your Ansible vault password (if using vault; skip if not)
1d. Add deploy key to Ansible inventory
Update your Ansible playbook or inventory so it can accept the key path as a variable. The GitHub Actions workflow will pass the key location at runtime.
Phase 2: Ansible Dashboard Role
2a. Create the dashboard Ansible role
This role should handle:
- Copy dashboard source files to server (or pull from repo on server)
- Build Docker image on server (or pull pre-built)
- Restart/recreate the dashboard container via Docker Compose
- Healthcheck to verify dashboard is responding
2b. Decision: Build on server vs build in CI?
Two options:
Option A — Build on server (simpler, recommended for v1):
- GitHub Actions SSHs in via Ansible
- Ansible pulls latest code on server, runs
docker compose up -d --build dashboard - No Docker registry needed
Option B — Build in CI, push to registry:
- GitHub Actions builds the Docker image
- Pushes to GitHub Container Registry (ghcr.io) or Docker Hub
- Ansible pulls the image on server
- More moving parts, better for multi-server setups
Recommendation: Start with Option A. You're deploying to one server.
2c. Tag the role
Ensure the role/playbook can be called with a tag like --tags dashboard
so it only touches the dashboard.
Phase 3: GitHub Actions Workflow
3a. Create workflow file
File: .github/workflows/deploy-dashboard.yml
name: Deploy Dashboard
on:
push:
branches: [main]
paths:
- 'dashboard/**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
- name: Install Ansible
run: pip install ansible
- name: Run Ansible playbook
run: |
ansible-playbook playbook.yml \
--tags dashboard \
--private-key ~/.ssh/deploy_key \
-u ${{ secrets.DEPLOY_USER }} \
-i "${{ secrets.DEPLOY_HOST }},"
env:
ANSIBLE_HOST_KEY_CHECKING: "false"
Key points:
paths: ['dashboard/**']— only triggers on changes to the dashboard directory- The inventory is inline (
-i "HOST,"with trailing comma) — no inventory file needed in the PBSII repo - Vault password step can be added if needed
3b. Test the workflow
- Push a small change to
dashboard/on main - Watch the Actions tab in GitHub
- Verify the dashboard redeploys on Linode
- Verify pushing to other directories does NOT trigger this workflow
Phase 4: Extend to Other Layers
Once the dashboard pipeline is solid, repeat the pattern:
4a. Collector pipeline
File: .github/workflows/deploy-collector.yml
Trigger: paths: ['collector/**']
Ansible role needs to:
- Build the Go binary (either on server or in CI)
- Copy binary to server
- Restart the systemd service
- Verify collector is responding on its HTTP API
Decision: Go cross-compile in CI (fast, clean) vs build on server. CI cross-compile is easy with Go and avoids needing Go installed on Linode.
4b. Parser pipeline
File: .github/workflows/deploy-parser.yml
Trigger: paths: ['parser/**']
Ansible role needs to:
- Copy parser source to server
- Ensure Python venv exists with correct deps (uv sync)
- No restart needed — n8n triggers it on schedule
Open Questions
- Does the PBSII repo need its own Ansible playbook, or should it hook into your existing main Ansible project? If separate, the playbook and roles live in the PBSII repo. If shared, GitHub Actions needs access to the Ansible repo too.
- Where does the dashboard Docker Compose service definition live — in the PBSII repo or in your main compose file on the server?
- Do you want deployment notifications? (e.g., GitHub Actions → Google Chat webhook on success/failure)
- Branch strategy: deploy only from
main, or also from astagingbranch to staging server?
Repo Structure After Implementation
pbsii/
├── .github/
│ └── workflows/
│ ├── deploy-dashboard.yml
│ ├── deploy-collector.yml
│ └── deploy-parser.yml
├── ansible/
│ ├── playbook.yml
│ └── roles/
│ ├── dashboard/
│ ├── collector/
│ └── parser/
├── collector/
├── parser/
├── dashboard/
├── n8n/
│ └── workflows/
└── sql/
└── schema.sql
Key Principles
- Each layer deploys independently — a change to the parser never touches the dashboard
- Ansible stays the deployment executor — GitHub Actions is just the trigger
- Build on server for v1 — move to CI builds later if needed
- Dedicated deploy key — revocable, scoped, no passphrase
- Start with dashboard, prove the pattern, then extend
...sent from Jenny & Travis