| name | flox-containers |
| description | Containerizing Flox environments with Docker/Podman. Use for creating container images, OCI exports, multi-stage builds, and deployment workflows. |
Flox Containerization Guide
Core Commands
flox containerize # Export to default tar file
flox containerize -f ./mycontainer.tar # Export to specific file
flox containerize --runtime docker # Export directly to Docker
flox containerize --runtime podman # Export directly to Podman
flox containerize -f - | docker load # Pipe to Docker
flox containerize --tag v1.0 # Tag container image
flox containerize -r owner/env # Containerize remote environment
Basic Usage
Export to File
# Export to file
flox containerize -f ./mycontainer.tar
docker load -i ./mycontainer.tar
# Or use default filename: {name}-container.tar
flox containerize
docker load -i myenv-container.tar
Export Directly to Runtime
# Auto-detects docker or podman
flox containerize --runtime docker
# Explicit runtime selection
flox containerize --runtime podman
Pipe to Stdout
# Pipe directly to Docker
flox containerize -f - | docker load
# With tagging
flox containerize --tag v1.0 -f - | docker load
How Containers Behave
Containers activate the Flox environment on startup (like flox activate):
- Interactive:
docker run -it <image>→ Bash shell with environment activated - Non-interactive:
docker run <image> <cmd>→ Runs command with environment activated (likeflox activate -- <cmd>) - All packages, variables, and hooks are available inside the container
Note: Flox sets an entrypoint that activates the environment, then runs cmd inside that activation.
Command Options
flox containerize
[-f <file>] # Output file (- for stdout); defaults to {name}-container.tar
[--runtime <runtime>] # docker/podman (auto-detects if not specified)
[--tag <tag>] # Container tag (e.g., v1.0, latest)
[-d <path>] # Path to .flox/ directory
[-r <owner/name>] # Remote environment from FloxHub
Manifest Configuration
Configure container in [containerize.config] (experimental):
[containerize.config]
user = "appuser" # Username or uid:gid format
exposed-ports = ["8080/tcp"] # Ports to expose (tcp/udp/default:tcp)
cmd = ["python", "app.py"] # Command to run (receives activated env)
volumes = ["/data", "/config"] # Mount points for persistent data
working-dir = "/app" # Working directory
labels = { version = "1.0" } # Arbitrary metadata
stop-signal = "SIGTERM" # Signal to stop container
Configuration Options Explained
user: Run container as specific user
- Username:
user = "appuser" - UID:GID:
user = "1000:1000"
exposed-ports: Network ports to expose
- TCP:
["8080/tcp"] - UDP:
["8125/udp"] - Default protocol is tcp:
["8080"]=["8080/tcp"]
cmd: Command to run in container
- Array form:
cmd = ["python", "app.py"] - Empty for service-based:
cmd = []
volumes: Mount points for persistent data
- List paths:
volumes = ["/data", "/config", "/logs"]
working-dir: Initial working directory
- Absolute path:
working-dir = "/app"
labels: Arbitrary metadata
- Key-value pairs:
labels = { version = "1.0", env = "production" }
stop-signal: Signal to stop container
- Common:
"SIGTERM","SIGINT","SIGKILL"
Complete Workflow Examples
Flask Web Application
# Create environment
flox init
flox install python311 flask
# Configure for container
cat >> .flox/env/manifest.toml << 'EOF'
[containerize.config]
exposed-ports = ["5000/tcp"]
cmd = ["python", "-m", "flask", "run", "--host=0.0.0.0"]
working-dir = "/app"
user = "flask"
EOF
# Build and run
flox containerize -f - | docker load
docker run -p 5000:5000 -v $(pwd):/app <container-id>
Node.js Application
flox init
flox install nodejs
cat >> .flox/env/manifest.toml << 'EOF'
[containerize.config]
exposed-ports = ["3000/tcp"]
cmd = ["npm", "start"]
working-dir = "/app"
EOF
flox containerize --tag myapp:latest --runtime docker
docker run -p 3000:3000 -v $(pwd):/app myapp:latest
Database Container
flox init
flox install postgresql
# Set up service in manifest
flox edit
# Add service and container config
cat >> .flox/env/manifest.toml << 'EOF'
[services.postgres]
command = '''
mkdir -p /data/postgres
if [ ! -d "/data/postgres/pgdata" ]; then
initdb -D /data/postgres/pgdata
fi
exec postgres -D /data/postgres/pgdata -h 0.0.0.0
'''
is-daemon = true
[containerize.config]
exposed-ports = ["5432/tcp"]
volumes = ["/data"]
cmd = [] # Service starts automatically
EOF
flox containerize -f - | docker load
docker run -p 5432:5432 -v pgdata:/data <container-id>
Common Patterns
Service Containers
Services start automatically when cmd is empty:
[services.web]
command = "python -m http.server 8000"
[containerize.config]
exposed-ports = ["8000/tcp"]
cmd = [] # Service starts automatically
Multi-Stage Pattern
Build in one environment, run in another:
# Build environment with all dev tools
cd build-env
flox activate -- flox build myapp
# Runtime environment with minimal deps
cd ../runtime-env
flox install myapp
flox containerize --tag production -f - | docker load
# Run
docker run production
Remote Environment Containers
Containerize shared team environments:
# Containerize remote environment
flox containerize -r team/python-ml --tag latest --runtime docker
# Run it
docker run -it team-python-ml:latest
Multi-Service Container
[services.db]
command = '''exec postgres -D "$FLOX_ENV_CACHE/postgres"'''
is-daemon = true
[services.cache]
command = '''exec redis-server'''
is-daemon = true
[services.api]
command = '''exec python -m uvicorn main:app --host 0.0.0.0'''
[containerize.config]
exposed-ports = ["8000/tcp", "5432/tcp", "6379/tcp"]
cmd = [] # All services start automatically
Platform-Specific Notes
macOS
- Requires docker/podman runtime (uses proxy container for builds)
- May prompt for file sharing permissions
- Creates
flox-nixvolume for caching - Safe to remove when not building:
docker volume rm flox-nix
Linux
- Direct image creation without proxy
- No intermediate volumes needed
- Native container support
Advanced Use Cases
Custom Entrypoint with Wrapper Script
[build.entrypoint]
command = '''
cat > $out/bin/entrypoint.sh << 'EOF'
#!/usr/bin/env bash
set -e
# Custom initialization
echo "Initializing application..."
setup_app
# Run whatever command was passed
exec "$@"
EOF
chmod +x $out/bin/entrypoint.sh
'''
[containerize.config]
cmd = ["entrypoint.sh", "python", "app.py"]
Health Check Support
[containerize.config]
cmd = ["python", "app.py"]
labels = {
"healthcheck" = "curl -f http://localhost:8000/health || exit 1"
}
Then in Docker:
docker run --health-cmd="curl -f http://localhost:8000/health || exit 1" \
--health-interval=30s \
myimage
Multi-Architecture Builds
Build for different architectures:
# On x86_64 Linux
flox containerize --tag myapp:amd64 --runtime docker
# On ARM64 (aarch64) Linux
flox containerize --tag myapp:arm64 --runtime docker
# Create manifest
docker manifest create myapp:latest \
myapp:amd64 \
myapp:arm64
Minimal Container Size
Create minimal runtime environment:
[install]
# Only runtime dependencies
python.pkg-path = "python311"
# No dev tools, no build tools
[build.app]
command = '''
# Build in build environment
python -m pip install --target=$out/lib/python -r requirements.txt
cp -r src $out/lib/python/
'''
runtime-packages = ["python"]
[containerize.config]
cmd = ["python", "-m", "myapp"]
Container Registry Workflows
Push to Registry
# Build container
flox containerize --tag myapp:v1.0 --runtime docker
# Tag for registry
docker tag myapp:v1.0 registry.company.com/myapp:v1.0
# Push
docker push registry.company.com/myapp:v1.0
GitLab CI/CD
containerize:
stage: build
script:
- flox containerize --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG --runtime docker
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
GitHub Actions
- name: Build container
run: |
flox containerize --tag ghcr.io/${{ github.repository }}:${{ github.sha }} --runtime docker
- name: Push to GHCR
run: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
Kubernetes Deployment
Basic Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: registry.company.com/myapp:v1.0
ports:
- containerPort: 8000
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: myapp-data
Service Definition
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8000
type: LoadBalancer
Debugging Container Issues
Inspect Container
# Run interactively
docker run -it --entrypoint /bin/bash <image-id>
# Check environment
docker run <image-id> env
# Check what's in the image
docker run <image-id> ls -la /
View Container Logs
# Follow logs
docker logs -f <container-id>
# Last 100 lines
docker logs --tail 100 <container-id>
Execute Commands in Running Container
# Get a shell
docker exec -it <container-id> /bin/bash
# Run specific command
docker exec <container-id> flox list
Best Practices
- Use specific tags: Avoid
latest, use semantic versioning - Minimize layers: Combine related operations in manifests
- Use .dockerignore equivalent: Only include necessary files in build context
- Health checks: Implement health check endpoints for services
- Security: Run as non-root user when possible
- Volumes: Use volumes for persistent data, not container filesystem
- Environment variables: Make configuration overridable via env vars
- Logging: Log to stdout/stderr, not files
Related Skills
- flox-environments - Creating environments to containerize
- flox-services - Running services in containers
- flox-builds - Building artifacts before containerizing
- flox-sharing - Containerizing remote environments