Enhanced base image build system with: Container Engine Support: - Auto-detect podman or docker (prefers podman if both installed) - All scripts work with either engine seamlessly - No configuration needed Docker Hub Integration: - Push to both GitLab and Docker Hub registries - GitLab: registry.gitlab.com/towerops/towerops/elixir-runtime - Docker Hub: docker.io/gmcintire/elixir-runtime - Both versioned and :latest tags pushed to each Build Script: - Detect and use $CONTAINER_CMD for all operations - Show which container engine is being used in output - Tag and push to both registries automatically Makefile: - Add CONTAINER_CMD variable with auto-detection - Add DOCKERHUB_REGISTRY configuration - Update all targets to use detected container engine - Add login-dockerhub target for Docker Hub authentication - Update push target to push to both registries Documentation: - Document podman/docker auto-detection - Update login instructions for both registries - Update troubleshooting with examples for both engines This makes the build system more flexible and accessible to users who prefer podman over docker, while also publishing to the public Docker Hub registry for easier access. |
||
|---|---|---|
| .. | ||
| .gitignore | ||
| build.sh | ||
| Dockerfile | ||
| Makefile | ||
| QUICKSTART.md | ||
| README.md | ||
| update.sh | ||
Minimal Debian Base Image for Elixir/Phoenix
This directory contains scripts to build a minimal Debian 13 (Trixie) base image with Erlang/OTP and Elixir runtime pre-installed.
Container Engine Support
The build scripts automatically detect and use either Podman or Docker, preferring Podman if both are installed. No configuration needed - just install your preferred container engine and the scripts will use it.
Why a Custom Base Image?
Building a custom base image provides several benefits:
- Faster CI/CD - Base image is cached, no need to reinstall Erlang/Elixir on every build
- Smaller Images - Only includes essential runtime dependencies
- Security - Regularly rebuild with latest security patches
- Consistency - Same runtime environment across dev/staging/production
Quick Start
# Build the image
make build
# Test it works
make test
# Push to registry
make push
Usage
Building the Base Image
# Using Makefile (recommended)
make build
# Or using the script directly
chmod +x build.sh
./build.sh
This builds a multi-stage Docker image with:
- Builder stage: Installs Erlang and Elixir from Erlang Solutions repository
- Final stage: Minimal Debian with only runtime dependencies
Updating System Packages
Run this weekly/monthly to get latest security updates:
# Using Makefile
make update
# Or using the script
./update.sh
This rebuilds the image with --no-cache --pull to ensure all packages are updated.
Testing the Image
make test
Verifies:
- Erlang/OTP is installed and working
- Elixir is installed and working
- Locale is configured correctly
Pushing to Registries
# Login to registries (one-time setup)
make login # GitLab Container Registry
make login-dockerhub # Docker Hub
# Push to both GitLab and Docker Hub
make push
Automatically pushes to both:
- GitLab Container Registry:
registry.gitlab.com/towerops/towerops/elixir-runtime - Docker Hub:
docker.io/gmcintire/elixir-runtime
Both versioned and :latest tags are pushed to each registry.
Image Tags
Each build creates three tags:
- Full version:
28.3-elixir-1.19.5-debian-trixie(pinned, immutable) - Latest:
latest(always points to most recent build) - Dated:
28.3-elixir-1.19.5-debian-trixie-2026-01-25(for rollback)
Using the Base Image
Update your main k8s/Dockerfile:
# Before
ARG RUNNER_IMAGE="docker.io/debian:trixie-20251229-slim"
# After
ARG RUNNER_IMAGE="registry.gitlab.com/towerops/towerops/elixir-runtime:28.3-elixir-1.19.5-debian-trixie"
Then remove the runtime installation steps (already in base image):
# Remove these lines - they're now in the base image
# RUN apt-get update && apt-get install -y erlang elixir ...
# RUN locale-gen ...
Image Size
The final image is approximately 150-200 MB (vs ~120 MB for plain Debian).
This is significantly smaller than:
- Official Elixir images (~400-500 MB)
- Debian with manually installed Erlang/Elixir (~300 MB)
What's Included
Erlang/OTP Runtime
- Latest Erlang/OTP from Erlang Solutions repository
- All essential runtime libraries (ssl, crypto, etc.)
- No build tools or dev dependencies
Elixir Runtime
- Latest Elixir release
- Mix build tool
- IEx interactive shell
System Packages (Minimal Set)
libssl3- SSL/TLS supportlibsctp1- SCTP protocol (required by Erlang)libncurses6- Terminal handlinglibstdc++6- C++ standard libraryca-certificates- SSL certificate validationlocales- Locale support (en_US.UTF-8)iputils-ping- Network diagnosticsiproute2- Network utilities
Configuration
Environment variables in build.sh:
REGISTRY=registry.gitlab.com/towerops/towerops
IMAGE_NAME=elixir-runtime
ERLANG_VERSION=28.3
ELIXIR_VERSION=1.19.5
DEBIAN_VERSION=trixie-20251229-slim
Override during build:
ERLANG_VERSION=29.0 ELIXIR_VERSION=1.20.0 make build
Maintenance Schedule
Recommended rebuild schedule:
- Weekly: Security updates (
make update) - Monthly: Review Erlang/Elixir versions, upgrade if needed
- As needed: When CVEs are announced for included packages
CI/CD Integration
Add to GitLab CI to rebuild weekly:
rebuild-base-image:
stage: build
only:
- schedules # Run on schedule
script:
- cd k8s/base-image
- make build
- make push
Troubleshooting
Image fails to build
# Check if Erlang Solutions repository is accessible
wget -O- https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc
# Try building with verbose output
docker build --progress=plain --no-cache -f Dockerfile .
Application fails with missing dependencies
Add missing package to Dockerfile:
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
your-missing-package \
&& rm -rf /var/lib/apt/lists/*
Then rebuild: make build
DNS resolution issues (unlikely with Debian)
If you experience DNS issues (should not happen with Debian, only Alpine):
- Check
/etc/resolv.confin running container - Verify Kubernetes DNS is working
- Consider switching to Debian (already using it)
Comparison: Debian vs Alpine
We use Debian instead of Alpine because:
✅ Debian Advantages:
- Glibc (standard C library) - better compatibility
- No DNS resolution issues with Erlang
- Larger package ecosystem
- Better tested with Erlang/Elixir
❌ Alpine Disadvantages:
- Musl libc - can cause subtle bugs with Erlang NIFs
- DNS resolution issues with
:inetand libcluster - Smaller package selection
- Only ~50 MB smaller (not worth the issues)