Container Strategy¶
Underworld3 provides two container deployment strategies for different use cases:
Binder containers - Pre-built images for mybinder.org web-based launches
Command-line containers - Lightweight images for local Docker/Podman use
Architecture Overview¶
underworld3 repository
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
Dockerfile.base Containerfile GitHub Actions
.optimized (micromamba) ┌─────────────┐
│ │ │binder-image │
│ │ │ .yml │
▼ ▼ └──────┬──────┘
GHCR (binder) GHCR (CLI) │
~3.4GB slim ~2GB ▼
│ uw3-binder-launcher
│ (auto-updated)
▼ │
mybinder.org ▼
mybinder.org
Binder Containers¶
Purpose¶
Binder containers are optimized for mybinder.org launches, providing:
Pre-compiled Underworld3 with all dependencies
Jupyter environment ready for notebooks
Fast launch times (no build required on mybinder.org)
Key Files¶
File |
Location |
Purpose |
|---|---|---|
|
|
Primary binder Dockerfile (~3.4GB) |
|
|
GitHub Actions build workflow |
|
|
Auto-update launcher |
Image Registry¶
Images are pushed to GitHub Container Registry (GHCR):
ghcr.io/underworldcode/uw3-base:<branch>-slim
ghcr.io/underworldcode/uw3-base:latest-slim
Branch-specific tags (release-0.99-slim, main-slim, development-slim) enable selecting specific versions. The default is the latest release branch.
Build Triggers¶
The binder-image.yml workflow triggers on:
Changes to
container/Dockerfile.base.optimizedChanges to
pixi.tomlorpixi.lockChanges to Cython files (
src/**/*.pyx,src/**/*.c)Changes to
setup.pyManual dispatch (with optional force rebuild)
Note
Pure Python changes (.py files) don’t trigger rebuilds because they’re pulled at runtime via nbgitpuller.
Layer Size Constraints¶
mybinder.org enforces ~1GB layer size limits. The optimized Dockerfile splits the lib directory across multiple layers to stay under this limit.
Launcher Repository¶
The uw3-binder-launcher repository serves as the mybinder.org entry point:
Structure:
uw3-binder-launcher/
├── .binder/
│ └── Dockerfile # FROM ghcr.io/underworldcode/uw3-base:<branch>-slim
├── .github/workflows/
│ └── update-image.yml # Auto-updates Dockerfile on new builds
└── README.md # Badge links and usage instructions
Branch Mapping:
Launcher Branch |
UW3 Source |
Binder URL |
Purpose |
|---|---|---|---|
|
tag |
|
Default — stable JOSS release |
|
tag |
|
Current release |
|
|
|
Latest tagged release |
|
|
|
Bleeding edge |
Automation Pipeline¶
When code is pushed to a tracked branch:
Build:
binder-image.ymlbuilds and pushes to GHCRDispatch: Sends
repository_dispatchevent to launcher repoUpdate: Launcher’s
update-image.ymlupdates the DockerfileReady: mybinder.org uses updated image on next launch
Tip
The LAUNCHER_PAT secret (Personal Access Token with repo scope) enables cross-repository dispatch.
Using nbgitpuller¶
Any repository can launch on mybinder.org using the pre-built image via nbgitpuller:
https://mybinder.org/v2/gh/underworldcode/uw3-binder-launcher/development?urlpath=git-pull%3Frepo%3Dhttps%253A%252F%252Fgithub.com%252FYOUR_ORG%252FYOUR_REPO%26urlpath%3Dlab%252Ftree%252FYOUR_REPO%252Fpath%252Fto%252Fnotebook.ipynb
Use the scripts/binder_wizard.py script to generate these URLs.
Command-Line Containers¶
Purpose¶
Command-line containers provide a lightweight option for users who want to run Underworld3 locally without installing dependencies:
Micromamba-based (smaller image size)
Jupyter server included
Volume mounting for data transfer
Key Files¶
File |
Location |
Purpose |
|---|---|---|
|
|
Micromamba-based image (~2GB) |
|
|
Podman launch script |
|
|
GHCR build workflow |
Building Locally¶
# With Podman
podman build . --rm \
--format docker \
-f ./container/Containerfile \
-t underworldcode/underworld3:local
# With Docker
docker build -f container/Containerfile -t underworldcode/underworld3:local .
Running¶
Using the launch script (recommended for Podman):
./container/launch-container.sh
This script:
Creates
$HOME/uw_spacefor file transferMaps it to
/home/mambauser/hostin the containerRuns Jupyter on port 10000:
http://localhost:10000Handles rootless Podman UID/GID mapping
Manual Docker run:
docker run -it --rm -p 8888:8888 ghcr.io/underworldcode/underworld3:development
Rootless Podman¶
The launch script includes UID/GID mapping for rootless Podman:
podman run -it --rm \
-p 10000:8888 \
--uidmap $uid:0:1 \
--uidmap 0:1:$uid \
# ... additional mappings
-v "${HOME}/uw_space":/home/mambauser/host \
ghcr.io/underworldcode/underworld3:development
Warning
Do NOT run the launch script with sudo. Rootless Podman requires the executing user to be non-root for proper namespace mapping.
Image Registry¶
Command-line images are pushed to GHCR (same registry as binder images):
ghcr.io/underworldcode/underworld3:<branch>
ghcr.io/underworldcode/underworld3:latest
Builds trigger on pushes to main and development branches when container-related files change. Can also be triggered manually via workflow_dispatch.
Comparison¶
Aspect |
Binder |
Command-Line |
|---|---|---|
Dockerfile |
|
|
Base |
Ubuntu + Pixi |
Micromamba |
Size |
~3.4GB (slim) |
~2GB |
Registry |
GHCR |
GHCR |
Use case |
mybinder.org |
Local |
Workflow |
|
|
Automation |
Full (build + launcher update) |
Build only |
Architecture Constraints¶
Platform Support¶
Currently only linux/amd64 is built because vtk-osmesa (required for headless rendering) is not available for ARM architectures.
When Rebuilds Are Required¶
Change Type |
Rebuild Required? |
Reason |
|---|---|---|
|
No |
Runtime pull works |
|
Yes |
Cython needs recompile |
|
Yes |
Dependencies changed |
|
Yes |
Build config changed |
Notebooks |
No |
Runtime pull works |
Documentation |
No |
Not in container |
Troubleshooting¶
GHCR Permission Errors¶
If you see permission_denied: write_package:
Go to the package settings on GHCR
Under “Manage Actions access”, add the repository with write permission
Ensure the repository is linked to the package
repository_dispatch Not Triggering¶
The update-image.yml workflow must be on the default branch (usually main) of the launcher repository to receive repository_dispatch events.
mybinder.org Layer Size Errors¶
If builds fail with layer size errors:
Check the
Dockerfile.base.optimizedlayer splittingEnsure no single layer exceeds ~1GB
Consider further splitting large directories