towerops/k8s/base-image
2026-07-16 07:54:28 -05:00
..
.gitignore feat: add custom minimal Debian base image build system 2026-01-25 09:07:55 -06:00
build.sh refactor: rename base images to gmcintire/towerops-base 2026-01-25 09:33:42 -06:00
Dockerfile update imagens 2026-07-16 07:54:28 -05:00
Makefile refactor: rename base images to gmcintire/towerops-base 2026-01-25 09:33:42 -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 fix: use official hexpm/elixir images instead of erlang-solutions repo 2026-01-25 09:21:42 -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: Uses official hexpm/elixir image with pre-built Erlang/Elixir
  • Final stage: Minimal Debian with only runtime dependencies copied from builder

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 hexpm images are accessible
docker pull hexpm/elixir:1.19.5-erlang-28.3-debian-trixie-20251229-slim

# 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