Skip to content

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)