--- created: 2026-04-25 path: Sources/Dev project: ansible-container-deploy status: active tags: - pbs - ansible - docker - deployment - automation type: project-plan updated: 2026-04-25 --- # Ansible Container Deploy Tag-based per-container Ansible deployment, CLI wizard enhancements, and GitHub clone integration for the PBS Docker stack. ## Background The wordpress-install repo (pbs-production/) has been refactored from a loop-based all-or-nothing Docker deployment to a tag-based `import_tasks` pattern. Each container has its own task wrapper under `tasks/containers/` that calls `common/deploy.yml` with container-specific variables. The docker-container repo (pbs-workshop/) houses a CLI wizard that scaffolds new container files (compose template, task wrapper, env template) but currently only generates simple-tier containers. The tag-based refactor is coded but untested. The wizard needs enhancements to support medium and complex tier containers. A separate GitHub deploy refactor (github-deploy-refactor, status: complete) established the pbsdeploybot machine user pattern for repo cloning — that logic needs to be absorbed into the dockers role. ## Repos - **wordpress-install (pbs-production/)** — production deployment repo, tag-based structure in place - **docker-container (pbs-workshop/)** — tooling repo, CLI wizard and scaffolding - **claude-code-scaffolding-skill** — separate project, consumes the patterns defined here (see Phase 5) ## Architecture ### common/deploy.yml Variable Contract Required: - `container_name` (str) — directory name under `files/` and deploy path at `{{ dockers_path }}//` Optional: - `container_has_env` (bool, default false) — templates `files//.env.j2` → `.env` - `container_extra_dirs` (list of str) — additional directories to create - `container_extra_files` (list of {src, dest, mode}) — static files to copy - `container_extra_templates` (list of {src, dest, mode}) — Jinja templates to render Global vars (from vars/main.yml and inventory): - `dockers_path` — base directory (`/opt/docker`) - `user` — system user for file ownership - `dockers_lan_name` — Docker network name - `dockers_domain_name` — primary domain ### Container Complexity Tiers **Simple** (redis, portainer, uptime-kuma): `container_name` only **Medium** (pbs-api): adds `container_extra_dirs`, `container_extra_files`, `container_extra_templates` **Complex** (wordpress): medium plus post-deploy tasks ### common/github.yml (planned) Will sit alongside `common/deploy.yml`. Containers that build from a cloned repo import `github.yml` first (clone/pull via pbsdeploybot), then `deploy.yml` (compose up). Uses the same SSH host alias pattern established in github-deploy-refactor. ## Phases ### Phase 1: Validate Tag-Based Deployment Test the existing tag-based refactor on staging. - [ ] Run `--tags traefik` against staging, verify only traefik deploys - [ ] Run `--tags wordpress` against staging, verify only wordpress deploys - [ ] Run `--tags "traefik,wordpress"` to verify multi-tag targeting - [ ] Run `--skip-tags n8n` to verify exclusion works - [ ] Run full playbook (no tags) to verify all-container deployment still works - [ ] Document any issues or adjustments needed ### Phase 2: Absorb GitHub Clone Logic Migrate the github role's clone logic into the dockers role as `common/github.yml`. - [ ] Create `tasks/common/github.yml` in the dockers role - [ ] Accept per-invocation vars: `repo_url`, `repo_dest`, `repo_version` - [ ] Use `become_user: "{{ ansible_user }}"` for SSH config access - [ ] Default `repo_version` to `main` - [ ] Update container task wrappers that need repo clones (e.g., pbs-hub) to import `github.yml` before `deploy.yml` - [ ] Test on staging with an existing repo-based container - [ ] Decide whether standalone github role is kept for non-container use cases or retired entirely ### Phase 3: Wizard Enhancements Extend the CLI wizard to support medium-tier containers and repo-based builds. #### 3a: Dockerfile Support - [ ] Add wizard prompt: "Does this container need a custom Dockerfile?" - [ ] If yes, generate a Dockerfile template in the container's files directory - [ ] Add Dockerfile to `container_extra_files` in the generated task wrapper #### 3b: Git Repo Support - [ ] Add wizard prompt: "Is this container built from a git repo?" - [ ] If yes, prompt for `repo_url` and `repo_version` - [ ] Generate task wrapper that imports `common/github.yml` before `common/deploy.yml` #### 3c: Extra Files/Dirs/Templates - [ ] Add wizard prompts for additional directories, static files, and templates - [ ] Generate `container_extra_dirs`, `container_extra_files`, `container_extra_templates` entries in the task wrapper #### 3d: Vault Secret References - [ ] When the wizard detects vars that need secrets (db passwords, API keys, etc.), generate placeholder references in compose templates using `{{ vault_ }}` syntax - [ ] Output a post-scaffold checklist reminding the user which vault vars need to be manually added to encrypted group vars #### 3e: Repo-Context Awareness - [ ] When the wizard is run inside the production repo, it should read existing `vars/main.yml`, network configs, and Traefik patterns to inform prompts and output - [ ] Wizard should auto-detect available networks, domain name, and base path from existing config - [ ] Wizard should append generated vars entries to `vars/main.yml` (or output them for manual paste) - [ ] Implementation details deferred to Claude Code — requirement is stated, approach is flexible ### Phase 4: Production Rollout - [ ] Deploy tag-based structure to production (after staging validation) - [ ] Test selective deployment on production with a low-risk container - [ ] Update any runbooks or deployment docs ### Phase 5: Scaffolding Skill Integration Separate project (claude-code-scaffolding-skill), documented here for dependency tracking. Three new scaffold types to add: - **`ansible-docker-container`** — scaffolds the project structure for a tag-based Docker container deployment (folder tree, template files, vars skeleton). The wizard then fills in the details. - **`ansible-role`** — reusable Galaxy-style role (defaults, handlers, meta, tasks, templates, Molecule testing) - **`ansible`** — full playbook project (ansible.cfg, inventory, group_vars, host_vars, playbooks, roles, requirements.yml, vault setup) The scaffold creates project structure; the wizard populates container-specific content. Scaffold = one-time project start, wizard = repeated container additions. This project provides the reference patterns. The scaffolding skill project owns implementation. ## Decisions Made - **Wizard vs scaffold split:** Scaffold generates project structure, wizard fills in container details. Different tools, same conventions. - **Vault secrets stay manual:** Wizard generates `{{ vault_* }}` placeholder references and a reminder checklist. Actual vault entries are hand-added. - **Context-aware wizard implementation deferred to Claude Code:** Requirement is that the wizard reads existing repo config when run in-place. How it does that is an implementation detail. - **Scaffolding skill: start inside existing skill, separate if it doesn't fit.** Build assuming merge into claude-code-scaffolding-skill. If infrastructure types feel forced next to dev tooling types, split into standalone skill. - **common/github.yml pattern:** Clone logic absorbed into dockers role, not kept as a standalone role for container use cases. Matches the common/deploy.yml pattern. ## Dependencies - github-deploy-refactor (complete) — provides pbsdeploybot auth pattern - claude-code-scaffolding-skill (separate project) — consumes patterns from Phase 3+ ## Open Questions - Should the standalone github role be kept for non-container clone use cases, or is pbsdeploybot + common/github.yml sufficient for everything? - Final call on scaffolding skill merge vs standalone — deferred until implementation reveals fit ...sent from Jenny & Travis