Container Runtime
The container runtime only supports Debian-based Linux container images (e.g., debian:stable-slim).
NOT supported:
- Alpine-based images — musl-based environments have subtle behavioral differences that reduce runtime reliability. Only images with exact name
alpineor ending with/alpine(after stripping tags/digests) are rejected. Images likego-alpine-builderormyorg/alpine-toolsare NOT rejected. - Windows container images (
mcr.microsoft.com/windows/*) — No POSIX shell available
Platform requirements:
- Linux: Works natively with Docker or Podman
- macOS: Works with Docker Desktop (uses Linux VMs internally)
- Windows: Requires Docker Desktop with WSL2 backend in Linux containers mode
Scripts are executed using /bin/sh inside the container, which requires a standard POSIX-compatible shell environment.
The container runtime executes commands inside a Docker or Podman container. It provides complete isolation and reproducibility - your command runs in the exact same environment every time.
How It Works
When you run a command with the container runtime, Invowk™:
- Pulls or builds the container image (if needed)
- Mounts the invowkfile's directory into the container
- Executes the script inside the container
- Streams output back to your terminal
Basic Usage
{
name: "build"
implementations: [
{
script: "go build -o /workspace/bin/app ./..."
runtimes: [{
name: "container"
image: "golang:1.26"
}]
platforms: [{name: "linux"}]
}
]
}
invowk cmd myproject build
Container Image Sources
You must specify either an image or a containerfile - they're mutually exclusive.
Pre-built Images
runtimes: [{
name: "container"
image: "golang:1.26"
}]
Common images:
debian:stable-slim- Minimal Debian (recommended base image)golang:1.26- Go developmentnode:20- Node.js developmentpython:3-slim- Python development
Custom Containerfile
Build from a local Containerfile/Dockerfile:
runtimes: [{
name: "container"
containerfile: "./Containerfile" // Relative to invowkfile
}]
Example Containerfile:
FROM golang:1.26
RUN apt-get update && apt-get install -y
make
git
WORKDIR /workspace
Volume Mounts
Mount additional directories into the container:
runtimes: [{
name: "container"
image: "golang:1.26"
volumes: [
"./data:/data", // Relative path
"/tmp:/tmp:ro", // Absolute path, read-only
"${HOME}/.cache:/cache" // Environment variable
]
}]
The invowkfile's directory is automatically mounted to /workspace.
Port Mappings
Expose container ports to the host:
runtimes: [{
name: "container"
image: "node:20"
ports: [
"3000:3000", // Host:Container
"8080:80" // Map container port 80 to host port 8080
]
}]
Using Interpreters
Like native runtime, containers support custom interpreters:
Auto-Detection from Shebang
{
name: "analyze"
implementations: [{
platforms: [{name: "linux"}]
script: """
#!/usr/bin/env python3
import sys
print(f"Python {sys.version} in container!")
"""
runtimes: [{
name: "container"
image: "python:3-slim"
}]
}]
}
Explicit Interpreter
{
name: "analyze"
implementations: [{
script: """
import sys
print(f"Running on Python {sys.version_info.major}")
"""
runtimes: [{
name: "container"
image: "python:3-slim"
interpreter: "python3"
}]
platforms: [{name: "linux"}]
}]
}
Environment Variables
Environment variables are passed into the container:
{
name: "deploy"
env: {
vars: {
DEPLOY_ENV: "production"
API_URL: "https://api.example.com"
}
}
implementations: [{
platforms: [{name: "linux"}]
script: """
echo "Deploying to $DEPLOY_ENV"
echo "API: $API_URL"
"""
runtimes: [{
name: "container"
image: "debian:stable-slim"
}]
}]
}
Host SSH Access
Sometimes your container needs to execute commands on the host system. Enable SSH access back to the host:
{
name: "deploy from container"
implementations: [{
script: """
# Connection credentials are provided via environment variables
echo "SSH Host: $INVOWK_SSH_HOST"
echo "SSH Port: $INVOWK_SSH_PORT"
# Connect back to host
sshpass -p $INVOWK_SSH_TOKEN ssh -o StrictHostKeyChecking=no
$INVOWK_SSH_USER@$INVOWK_SSH_HOST -p $INVOWK_SSH_PORT
'echo "Hello from host!"'
"""
runtimes: [{
name: "container"
image: "debian:stable-slim"
enable_host_ssh: true // Enable SSH server
}]
platforms: [{name: "linux"}]
}]
}
SSH Environment Variables
When enable_host_ssh: true, these variables are available:
| Variable | Description |
|---|---|
INVOWK_SSH_ENABLED | Set to true when SSH is active |
INVOWK_SSH_HOST | Host address (e.g., host.docker.internal) |
INVOWK_SSH_PORT | SSH server port |
INVOWK_SSH_USER | Username (invowk) |
INVOWK_SSH_TOKEN | One-time authentication token |
Security
- Each command execution gets a unique token
- Tokens are revoked when the command completes
- The SSH server only accepts token-based authentication
- The server shuts down after command execution
Container Requirements
Your container needs sshpass or similar for password-based SSH:
FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
openssh-client sshpass \
&& rm -rf /var/lib/apt/lists/*
Dependencies
Container dependencies are validated inside the container:
{
name: "build"
depends_on: {
tools: [
// Checked inside the container, not on host
{alternatives: ["go"]},
{alternatives: ["make"]}
]
filepaths: [
// Paths relative to container's /workspace
{alternatives: ["go.mod"]}
]
}
implementations: [{
platforms: [{name: "linux"}]
script: "make build"
runtimes: [{
name: "container"
image: "golang:1.26"
}]
}]
}
Container Engine
Invowk supports both Docker and Podman. Configure your preference:
// ~/.config/invowk/config.cue
container_engine: "podman" // or "docker"
If not configured, Invowk tries:
podman(if available)docker(fallback)
Working Directory
By default, the invowkfile's directory is mounted to /workspace and used as the working directory:
{
name: "build"
implementations: [{
platforms: [{name: "linux"}]
script: """
pwd # Outputs: /workspace
ls # Shows your project files
"""
runtimes: [{
name: "container"
image: "debian:stable-slim"
}]
}]
}
Override with workdir:
{
name: "build frontend"
workdir: "./frontend" // Mounted and used as workdir
implementations: [{
platforms: [{name: "linux"}]
script: "npm run build"
runtimes: [{
name: "container"
image: "node:20"
}]
}]
}
Complete Example
Here's a full-featured container command:
{
name: "build and test"
description: "Build and test in isolated container"
env: {
vars: {
GO_ENV: "test"
CGO_ENABLED: "0"
}
}
depends_on: {
tools: [{alternatives: ["go"]}]
filepaths: [{alternatives: ["go.mod"]}]
}
implementations: [{
script: """
echo "Go version: $(go version)"
echo "Building..."
go build -o /workspace/bin/app ./...
echo "Testing..."
go test -v ./...
echo "Done!"
"""
runtimes: [{
name: "container"
image: "golang:1.26"
volumes: [
"${HOME}/go/pkg/mod:/go/pkg/mod:ro" // Cache Go dependencies
]
}]
platforms: [{name: "linux"}, {name: "macos"}]
}]
}
Auto-provisioning
When container.auto_provision.enabled is true (default), Invowk builds a cached, derived image by attaching a small provisioned layer on top of your base image (either the image you specify or the image built from your Containerfile/Dockerfile). That layer includes the invowk binary and any modules, so invowk commands are available inside the container.
Auto-provisioning runs for every container execution, not just interactive mode. The derived image is cached as invowk-provisioned:<hash> and reused; if provisioning fails, Invowk warns and runs the base image instead.
Interactive Mode
Container runtime fully supports interactive mode (-i). Interactive runs use the same provisioned image layer described above (when enabled) and add host-side TUI plumbing, enabling:
- TUI components as modal overlays
- Full PTY support for password prompts and confirmations
- Seamless integration with host terminal
# Run a container command interactively
invowk cmd myproject build -i -r container
When you run with -i, Invowk:
- Uses the provisioned image layer (if enabled)
- Starts a TUI server on the host
- Forwards TUI requests from container to host
- Renders overlays on your terminal
This means your scripts can use invowk tui commands inside containers:
{
name: "deploy container"
implementations: [{
platforms: [{name: "linux"}]
script: """
# This TUI confirm appears as an overlay on your terminal
if invowk tui confirm "Deploy to production?"; then
echo "Deploying..."
./deploy.sh
fi
"""
runtimes: [{
name: "container"
image: "debian:stable-slim"
}]
}]
}
The provisioned image is cached as invowk-provisioned:<hash>, so subsequent runs are fast.
Advantages
- Reproducibility: Same environment everywhere
- Isolation: No host system pollution
- Version control: Pin exact tool versions
- CI/CD parity: Local builds match CI builds
- Clean builds: Fresh environment each time
Limitations
- Performance: Container startup overhead
- Disk space: Images consume storage
- Complexity: Need to manage images
- Host access: Limited without SSH bridge
When to Use Container
- Reproducible builds: When consistency matters
- CI/CD pipelines: Match local and CI environments
- Legacy projects: Isolate old tool versions
- Team onboarding: No local tool installation needed
- Clean-room builds: Test without host pollution
Troubleshooting
Container Not Starting
# Check if container engine is available
docker --version # or: podman --version
# Check if image exists
docker images | grep golang
Slow First Run
The first run pulls the image. Subsequent runs are faster:
# Pre-pull images
docker pull golang:1.26
docker pull node:20
Permission Issues
On Linux, you may need to configure container permissions:
# For Docker
sudo usermod -aG docker $USER
# For Podman (rootless)
# Usually works out of the box
Next Steps
- Native Runtime - For development speed
- Virtual Runtime - For cross-platform scripts
- Dependencies - Declare command requirements