towerops/k8s/base-image
Graham McIntire d92443d91b
feat: add podman support and Docker Hub push to base image builder
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.
2026-01-25 09:18:03 -06:00
..
.gitignore feat: add custom minimal Debian base image build system 2026-01-25 09:07:55 -06:00
build.sh feat: add podman support and Docker Hub push to base image builder 2026-01-25 09:18:03 -06:00
Dockerfile feat: add custom minimal Debian base image build system 2026-01-25 09:07:55 -06:00
Makefile feat: add podman support and Docker Hub push to base image builder 2026-01-25 09:18:03 -06:00
QUICKSTART.md feat: add podman support and Docker Hub push to base image builder 2026-01-25 09:18:03 -06:00
README.md feat: add podman support and Docker Hub push to base image builder 2026-01-25 09:18:03 -06:00
update.sh feat: add custom minimal Debian base image build system 2026-01-25 09:07:55 -06:00

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:

  1. Faster CI/CD - Base image is cached, no need to reinstall Erlang/Elixir on every build
  2. Smaller Images - Only includes essential runtime dependencies
  3. Security - Regularly rebuild with latest security patches
  4. 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:

  1. Full version: 28.3-elixir-1.19.5-debian-trixie (pinned, immutable)
  2. Latest: latest (always points to most recent build)
  3. 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 support
  • libsctp1 - SCTP protocol (required by Erlang)
  • libncurses6 - Terminal handling
  • libstdc++6 - C++ standard library
  • ca-certificates - SSL certificate validation
  • locales - Locale support (en_US.UTF-8)
  • iputils-ping - Network diagnostics
  • iproute2 - 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):

  1. Check /etc/resolv.conf in running container
  2. Verify Kubernetes DNS is working
  3. 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 :inet and libcluster
  • Smaller package selection
  • Only ~50 MB smaller (not worth the issues)

See Also