Skip to content

User Guide: Flavors and Overlays

This guide explains how to use pre-built flavors and create custom overlays to extend Nyia Keeper assistants with additional tools and packages.

Quick Decision: Flavor or Overlay?

I need... Use Command
Python dev tools (pytest, black, mypy) Flavor nyia-claude --flavor python
PHP dev tools (PHPUnit, PHPStan) Flavor nyia-claude --flavor php
Node.js / React / Cypress / Expo Flavor nyia-claude --flavor node
PHP + React fullstack Flavor nyia-claude --flavor php-react
Rust + Tauri v2 desktop apps Flavor nyia-claude --flavor rust-tauri
Something else (custom packages) Overlay Create Dockerfile, then --build-custom-image

Rule of thumb: - Flavor = ready to use, no build required - Overlay = custom, requires Docker and building


Part 1: Using Pre-built Flavors

Flavors are specialized images pre-built with common development tools. They're pulled automatically from the registry - no local build required.

List Available Flavors

nyia-claude --list-flavors

Current Flavors

Flavor Tools Included
python pytest, pytest-cov, black, mypy, ruff, isort, ipython
php PHP 8.3, Composer, PHPUnit, PHPStan, PHP-CS-Fixer
node Node.js 22, yarn, pnpm, typescript, biome, vitest, vite, storybook, cypress (headless Chromium), Expo (via npx expo), eas-cli
php-react PHP 8.2 + React + Storybook + Jest + Cypress + PHPUnit (fullstack)
rust-tauri Rust, Cargo, Tauri v2 CLI, clippy, rustfmt, cargo-watch, Node.js 22

Using a Flavor

# Python development
nyia-claude --flavor python

# PHP development
nyia-claude --flavor php

# Interactive mode with flavor
nyia-claude --flavor python

The first time you use a flavor, it will be pulled from the registry automatically.


Part 2: Creating Custom Overlays

If you need packages not included in any flavor, create a custom overlay.

Requirements: Docker must be installed.

Step 1: Create Your Overlay Dockerfile

Choose one of these locations:

Location Scope
~/.config/nyiakeeper/claude/overlay/Dockerfile All your projects
.nyiakeeper/claude/overlay/Dockerfile This project only

Create your Dockerfile:

mkdir -p ~/.config/nyiakeeper/claude/overlay/

cat > ~/.config/nyiakeeper/claude/overlay/Dockerfile << 'EOF'
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

# Install system packages (as root)
USER root
RUN apt-get update && apt-get install -y \
    your-package \
    another-package \
    && rm -rf /var/lib/apt/lists/*

# Install user packages (as node)
USER node
RUN pip install --no-cache-dir your-python-package
EOF

Step 2: Build Your Custom Image

nyia-claude --build-custom-image

# Force rebuild without Docker cache
nyia-claude --build-custom-image --no-cache

This creates a project-scoped image named nyiakeeper/claude-custom-{project} (e.g., nyiakeeper/claude-custom-my-app). If only a user overlay exists (no project overlay), the global name nyiakeeper/claude-custom is used instead.

Step 3: Use Your Custom Image

# The build output shows the exact image name to use
nyia-claude --image nyiakeeper/claude-custom-my-app

Note: You must use --image to select your custom image. Without it, the default image is used.

Migration: Users who built legacy custom images (nyiakeeper/claude-custom) before project scoping can still reference them via the --image flag.

Step 4: Rebuild After an Update

Your custom image is built on top of the base image as it was at build time. When the base changes (nyia update pulled a new image, or you pulled or rebuilt it yourself), the custom image keeps the old base, with the old skills, prompts and tools, until you rebuild it:

nyia-claude --build-custom-image                  # for --flavor custom
nyia-claude --build-custom-image --flavor php     # for --flavor php-custom

You do not need to remember this: every --flavor custom / --flavor <base>-custom launch compares the custom image with its base and warns when the base has moved (see Custom image built from an older base). The launch still starts; nothing is rebuilt for you. A launch with --image <name> runs the image you named, with no check.


Overlay Stacking

If you have overlays in both locations, they're applied in order:

  1. Base image (from registry)
  2. User overlay (~/.config/nyiakeeper/claude/overlay/)
  3. Project overlay (.nyiakeeper/claude/overlay/)

This allows global preferences plus project-specific additions.


Dockerfile Best Practices

Always Switch Users Properly

USER root
# Install system packages here
RUN apt-get update && apt-get install -y package

USER node
# Install user packages here
RUN pip install package

Clean Up Package Caches

RUN apt-get update && apt-get install -y package \
    && rm -rf /var/lib/apt/lists/*

Use --no-cache-dir for pip

RUN pip install --no-cache-dir package

Don't Override the Entrypoint

The base image has a configured entrypoint. Don't change it unless you know what you're doing.


Troubleshooting

"Flavor not found"

❌ Error: Flavor 'nodejs' not found

Solution: Check available flavors with --list-flavors. Did you mean node instead of nodejs?

"Cannot pull image"

Error response from daemon: pull access denied

Solutions: - Check your network connection - Try docker login ghcr.io if authentication is required - Verify the image exists: docker manifest inspect ghcr.io/kaizendofr/nyiakeeper-claude-python:latest

Build fails with permission error

Permission denied: /some/path

Solution: Make sure you switch to USER node before installing user packages, and USER root for system packages.

Custom image not found

❌ Custom image not found locally: nyiakeeper/claude-custom-myproject:latest
❌ Build it first:  nyia-claude --build-custom-image
❌ Or run it explicitly:  nyia-claude --image nyiakeeper/claude-custom-myproject:latest
❌ Custom images are project-scoped: this name belongs to project 'myproject' — an image built in a different project directory uses a different tag

Solution: Run the command the error suggests — --build-custom-image builds the image for this project. Custom image names are project-scoped: a slug from the project folder name is appended only when that project has its own .nyiakeeper/<assistant>/overlay/Dockerfile. An image you built in another directory therefore has a different tag and will not be found here. List what you actually have with nyia-claude --list-images (note the leading --).

For a based custom flavor (e.g. php-custom) the build line names the base instead: nyia-claude --build-custom-image --flavor php.

Custom image built from an older base

⚠️  Custom image nyiakeeper/claude-custom:latest was built from an older base
⚠️    base nyiakeeper/claude:latest: built on 3f1c9a2b7d40, now 9e02b4c1a8f3
⚠️    rebuild it to get the updated base:  nyia-claude --build-custom-image
⚠️    (launching the current custom image anyway)

Meaning: the base image was updated after you built your custom image (typically by nyia update), so the session runs the previous base. The two short ids are the base image the custom image was built on and the one you have now.

Solution: run the rebuild command from the message. The next launch is silent again.

After nyia update the base usually has a new version tag, so the warning names the two images instead:

⚠️  Custom image nyiakeeper/claude-custom:latest was built on a different base than this launcher uses
⚠️    built on:       ghcr.io/kaizendofr/nyiakeeper-claude:v0.1.0-beta.16
⚠️    launcher uses:  ghcr.io/kaizendofr/nyiakeeper-claude:v0.1.0-beta.17
⚠️    rebuild it to get the current base:  nyia-claude --build-custom-image
⚠️    (launching the current custom image anyway)

The same warning appears after a channel switch (for example alpha to beta). The fix is the same rebuild.

Two related one-line hints:

  • ... has no base record (built before base tracking); rebuild it once ...: the image was built by an older Nyia Keeper that did not record its base. Rebuild it once; from then on base updates are detected.
  • ... is based on <old ref>; this launcher now uses <new ref>: the base comes from another repository or registry than before (for example a custom NYIA_REGISTRY). Rebuild if you want the custom image on the new base.

--build-custom-image reads the current base image; if it is not on your machine it is pulled first, and the build stops with an error when it cannot be pulled.


Quick Reference

Task Command
List flavors nyia-claude --list-flavors
Use Python flavor nyia-claude --flavor python
Use PHP flavor nyia-claude --flavor php
Build custom overlay nyia-claude --build-custom-image
Rebuild without cache nyia-claude --build-custom-image --no-cache
Rebuild after nyia update nyia-claude --build-custom-image [--flavor <base>]
Use custom image nyia-claude --image nyiakeeper/claude-custom-my-app
Check available images nyia-claude --list-images