Containerize Apps for ALPON with Dockerfile Templates
This guide is designed to help you quickly and easily containerize your applications on the ALPON devices.
Containerize Apps for ALPON with Dockerfile Templates
Package your application as an ARM64 Docker image that runs on both ALPON X5 AI and ALPON X4. This guide walks through preparing your project, writing the Dockerfile, and using two ready-made templates (Debian for development, Alpine for production) so the resulting image deploys cleanly through Sixfab Connect.
Place your source, dependencies, and a .dockerignore in one project folder, then write a
Dockerfile that picks an ARM64 base image, sets a working directory, copies your code,
installs dependencies, and declares the startup command. Build for ARM64 with
docker buildx build --platform linux/arm64, then push the image to the
Sixfab Container Registry at cr.sixfab.io. From there, deploy it to any
ALPON X5 AI or ALPON X4 through Sixfab Connect.
Both ALPON X5 AI (Raspberry Pi CM5 · Cortex-A76) and ALPON X4 (Raspberry Pi CM4 · Cortex-A72) run on
ARM64. Every image you deploy must include a linux/arm64 variant. Most official
images on Docker Hub (nginx, redis, postgres, node, python) already ship multi-arch manifests. Custom
images built on an x86 workstation must use docker buildx build --platform linux/arm64 — a
plain docker build on a Mac or PC will produce an x86 image that fails to start on ALPON with
exec format error.
Why ALPON for containerization
Every ALPON device ships with a Docker runtime preinstalled and authenticated against the Sixfab Container
Registry out of the box. There is no SSH setup, no per-device credential rotation, and no manual
docker pull. The two devices in the line share the same ARM64 build target, so a single image
works on both:
apt install docker, no daemon config.
linux/arm64 image runs on both ALPON X5 AI and ALPON X4 without changes.
Traditional vs. containerized application management
In a traditional setup each application sits on its own OS or virtual machine, brings its own dependencies,
and is updated through manual scp + systemctl rituals. Scaling means provisioning more
hosts; debugging means dialling into individual devices. On a fleet of edge devices that pattern collapses fast.
Containers flip the model. The application and every dependency it needs are baked into one immutable image. The same image runs identically on every device, deployments take seconds, and rollbacks are a tag change — not a hot-fix shell session. For ALPON fleets, that is the only model we recommend.
| Concern | Traditional | Containerized on ALPON |
|---|---|---|
| Dependencies | Installed per device, drift over time | Frozen in the image, identical on every device |
| Deployment | Manual file transfer, restart scripts | One-click from Sixfab Connect, seconds per device |
| Rollback | Reinstall older binaries, debug breakage | Redeploy previous image tag, instant |
| Isolation | Shared user-space, dependency conflicts | Each container runs in its own namespace |
| Fleet update | SSH loop across devices | Push new tag, deploy from Connect |
Prepare the project for containerization
Before writing the Dockerfile, get the project folder in shape. The image build process copies whatever is in this directory into the container, so anything that belongs out should be excluded explicitly.
-
1
List every dependency
Every library, runtime, and system tool the app needs must be declared somewhere the build can read. Use the convention for your language:
- Python —
requirements.txt(orpyproject.tomlfor Poetry/uv). - Node.js —
package.json+package-lock.json. - Go —
go.mod+go.sum. - System packages — list them in the Dockerfile
RUN apt-get install(Debian) orRUN apk add(Alpine) line.
A reference Python project layout:
project layout treemy_app/ ├── app.py # Main application entrypoint ├── requirements.txt # Python dependencies ├── Dockerfile └── .dockerignore # Files excluded from the image
- Python —
-
2
Add a
.dockerignorefileWithout a
.dockerignore, every log file, secret, and IDE folder gets copied into the image. That bloats the size, slows pulls over cellular, and can leak credentials. Treat it like a.gitignorefor the build context:.dockerignore ignore# Logs and runtime artefacts *.log *.pyc __pycache__/ # Tests and dev tooling tests/ .vscode/ .idea/ # Secrets and local config .env .env.* # Version control .git/ .gitignore
-
3
Identify the runtime surface
Decide upfront what the container needs from the host. These three answers shape the deployment form on Sixfab Connect later:
- Environment variables — anything that changes between dev and prod (database URLs, API keys, feature flags). Never hard-code these in the source.
- Ports — which TCP/UDP port the app listens on inside the container. On ALPON, the
host port must sit in the
30000–32767range; the container-side port is free. - Volumes — directories the container needs to read or write on the host (persistent storage, device files, configs). Plan these as Docker volume mounts, not bind mounts to specific host paths.
Write the Dockerfile
The Dockerfile is the recipe for the image. Each instruction creates a new image layer; ordered correctly, it lets Docker cache the slow steps and rebuild only what changed. The five instructions below cover the vast majority of edge applications.
| Instruction | What it does |
|---|---|
FROM |
Picks the base image. Always pin a tag (e.g. python:3.11-slim), never use latest in production. |
WORKDIR |
Sets the working directory inside the container. Subsequent instructions run here. |
COPY |
Copies files from the build context into the image. Copy requirements.txt before the rest of the source to maximise cache reuse. |
RUN |
Executes a shell command at build time. Use it to install system packages and language dependencies. |
CMD |
Defines the command that runs when the container starts. Always exec-form: CMD ["python", "app.py"]. |
A minimal Python Dockerfile, walked through
Pick the base image. The -slim variants are far smaller than the default and still include
enough to install most pure-Python packages:
# Pinned, slim, ARM64-compatible Python runtime FROM python:3.11-slim
Set the working directory. Every COPY and RUN that follows resolves relative to it:
WORKDIR /app
Copy requirements.txt first, install, then copy the rest of the source. This ordering means
Docker only rebuilds the dependency layer when requirements.txt changes — code-only edits skip
the slow pip install step entirely:
COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . .
Declare the startup command. Use exec-form (the JSON array, not the shell string) so signals like
SIGTERM reach the app cleanly when the container stops:
CMD ["python", "app.py"]
The complete Dockerfile
Combine the pieces above:
# Pinned, slim, ARM64-compatible Python runtime FROM python:3.11-slim # Working directory for the rest of the build WORKDIR /app # Install dependencies first — better cache reuse COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy the rest of the source COPY . . # Start the application CMD ["python", "app.py"]
Build it for ARM64
From the project root on your workstation:
# 1. Enable buildx (one-time, if not already) docker buildx create --use # 2. Build the ARM64 image and load it locally docker buildx build --load --platform linux/arm64 -t my-app:latest .
Dockerfile templates
Two ready-made templates cover the two phases of work. Use the Debian template while iterating; switch to the Alpine template before shipping to a fleet.
Built on debian:bookworm-slim. Includes the build toolchain and git so you can compile
native modules, debug from inside the container, and tweak dependencies without rebuilding the base.
- Includes
build-essential,git - Familiar
aptecosystem - Larger image (~150–300 MB) — fine for dev
Built on alpine:latest. Stripped down to the minimum runtime, no build tools, no shell
utilities you don't need. Pulls faster over cellular and presents a smaller attack surface.
- Uses
apkpackage manager - Tiny image (~5–50 MB typical)
- Faster pulls, less to patch
Development Dockerfile (Debian-based)
Drop this in the project root during active development:
# Development Dockerfile — Debian-based FROM debian:bookworm-slim WORKDIR /app # Build toolchain and dev utilities RUN apt-get update && apt-get install -y build-essential git && rm -rf /var/lib/apt/lists/* COPY . . # Mark the runtime environment ENV APP_ENV=development # Replace with your dev entrypoint CMD ["./your-dev-app"]
Production Dockerfile (Alpine-based)
Switch to this template once the app is stable. It optimises for image size, pull time, and the smaller attack surface that matters on a fleet of edge devices:
# Production Dockerfile — Alpine-based FROM alpine:latest WORKDIR /app # Runtime-only dependencies, no build tools RUN apk add --no-cache libc6-compat && rm -rf /var/cache/apk/* COPY . . # Mark the runtime environment ENV APP_ENV=production # Replace with your prod entrypoint CMD ["./your-prod-app"]
Alpine uses apk; Debian uses apt. Package names also differ — Debian's
libssl-dev is Alpine's openssl-dev; Debian's libpq-dev is Alpine's
postgresql-dev. When porting from the dev template to prod, look up each apt
package on pkgs.alpinelinux.org
and replace it with the Alpine equivalent. Native C extensions built against glibc may also need
libc6-compat on Alpine (musl).
Best practices for ALPON containers
The same image runs on both ALPON X5 AI and ALPON X4, but small choices in the Dockerfile decide whether it pulls in 10 seconds or 10 minutes over a cellular link, and whether it survives the next dependency update.
A polished, production-ready example
Applying the practices above to a real Dockerfile:
# Pinned, slim, ARM64 base FROM python:3.11-slim WORKDIR /app # System packages and Python deps in one layer, with cleanup RUN apt-get update && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/* # Dependencies first — cached unless requirements.txt changes COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Then the source COPY . . # Runtime configuration via ENV (override on the deploy form) ENV APP_ENV=production LOG_LEVEL=info # Exec-form CMD — clean signal handling CMD ["python", "app.py"]
Next steps: push and deploy
The image is built and tested locally. Two things remain: push it to the Sixfab Container Registry, then
deploy it from Sixfab Connect. Both are covered end-to-end on the Deploy Applications guide, including the
exact docker login + docker tag + docker push sequence and the deploy
form fields (ports, environment variables, volumes, network mode).
Updated about 22 hours ago
