Blackcairn – Git + Portainer Deployment Summary¶
Goal¶
Move all infrastructure definitions and content into Git so that:
- Git is the single source of truth
- Any PC can be used to edit content/config
- Portainer handles container lifecycle
- Content/config updates do not require container redeploys
- Secrets are never stored in Git
Git Repository¶
- Repo:
blackcairn-infra(private, GitHub) - Local path on Blackcairn:
~/blackcairn-infra
Repo structure (current)¶
blackcairn-infra/
├── stacks/
│ ├── nginx-proxy-manager/
│ │ └── docker-compose.yml
│ ├── mealie/
│ │ └── docker-compose.yml
│ ├── homepage/
│ │ ├── docker-compose.yml
│ │ ├── config/
│ │ │ ├── services.yaml
│ │ │ ├── widgets.yaml
│ │ │ ├── settings.yaml
│ │ │ └── ...
│ │ ├── .gitignore
│ │ └── .env.example
│ ├── blackcairn-splash/
│ │ ├── docker-compose.yml
│ │ ├── site/
│ │ │ ├── index.html
│ │ │ ├── assets/
│ │ │ └── ...
│ │ └── .gitignore
├── scripts/
│ ├── publish-splash.sh
│ ├── publish-homepage-config.sh
│ └── README.md
└── README.md
Portainer Strategy¶
What is deployed from Git via Portainer¶
Homepage
- Git-backed Portainer stack
- Container lifecycle managed by Portainer
- Uses environment variables in Portainer for secrets
What is not deployed from Git via Portainer¶
Splash screen
- Container is static (
nginx:alpine) - Content changes frequently
- Container redeploy is unnecessary for content changes
- This is intentional separation of concerns
Homepage Details¶
Container¶
- Name:
blackcairn-portal-git - Image:
ghcr.io/gethomepage/homepage:latest - Network:
proxy - Config bind mount:
/srv/docker/blackcairn-portal/config
Secrets handling¶
- No passwords in Git
- YAML uses env placeholders, e.g.:
password: ${HOMEPAGE_NPM_ADMIN_PASSWORD}- Real values are provided in Portainer stack environment variables
Content workflow¶
- Edit YAML in Git:
stacks/homepage/config - Commit + push
- On Blackcairn:
git pull
./scripts/publish-homepage-config.sh --apply
Splash Screen Details¶
Container¶
- Name:
blackcairn-splash - Image:
nginx:alpine - Network:
proxy - Bind mount:
/srv/www/blackcairn → /usr/share/nginx/html(read-only)
Content workflow¶
- Splash HTML/CSS/assets stored in Git:
stacks/blackcairn-splash/site - Publish via script:
./scripts/publish-splash.sh --apply
- No container redeploy required
Publish Scripts¶
publish-splash.sh¶
- Syncs:
stacks/blackcairn-splash/site → /srv/www/blackcairn - Uses
rsync --delete - Supports:
--dry-run--apply--restart(optional)
publish-homepage-config.sh¶
- Syncs:
stacks/homepage/config → /srv/docker/blackcairn-portal/config - Excludes logs
- Supports:
--dry-run--apply--restart(rarely needed)
Scripts README¶
scripts/README.md documents:
- Usage
- Safety rules
- Philosophy
- Secret handling
Daily Workflow (Authoritative)¶
On any PC¶
git clone https://github.com/blackcairn-ops/blackcairn-infra.git
# edit files
git add .
git commit -m "Describe change"
git push
On Blackcairn¶
cd ~/blackcairn-infra
git pull
./scripts/publish-homepage-config.sh --apply
./scripts/publish-splash.sh --apply
Container redeploys only happen when infrastructure changes, not content.
Philosophy (Key Takeaway)¶
- Git = blueprint
- Portainer = hands
- Containers change rarely
- Content/config changes often
- Never redeploy containers for content
- Never store secrets in Git
This is a deliberate, maintainable GitOps-lite setup.
Next steps (optional)¶
If you paste this into a new chat, we can immediately continue with:
- Converting Mealie to Git deployment
- Converting NPM to Git deployment
- Adding per-stack READMEs
- Tightening the workflow further (Makefile, safety checks, backups)