Update Containers from Sixfab Registry

Automated and manual container updates from Sixfab Registry

Push a new container image to the Sixfab Container Registry and roll it out to your ALPON X5 AI and ALPON X4 fleet two ways: manually from the ALPON Cloud dashboard, or automatically from CI/CD using image tags and the Sixfab Developer API. This guide covers production-grade Dockerfile templates, tagging strategy, and rollback procedures for both paths.

ALPON X5 AI ALPON X4 Docker · ARM64 cr.sixfab.io CI/CD ready
ALPON · Tutorial · Container updates from Sixfab Registry
How do I update a container running on ALPON?

Build your image for linux/arm64, push it to cr.sixfab.io/<your_username>/<image>:<tag>, then roll it out. For a manual update, open the device's Applications tab on ALPON Cloud and click Restart (for mutable tags like :latest) or Edit Deployment (to switch to an immutable tag like :v1.2.3). For an automated update, push the new tag from CI/CD and trigger a restart with the Sixfab Developer API. ALPON devices auto-authenticate against cr.sixfab.io, so no per-device credential setup is needed.

ARM64 architecture

Both ALPON X5 AI (Raspberry Pi CM5, Cortex-A76) and ALPON X4 (Raspberry Pi CM4, Cortex-A72) run on ARM64. Every container image you push to cr.sixfab.io must include a linux/arm64 variant. Custom images must be built with docker buildx build --platform linux/arm64. Architecture mismatches surface at container start as exec format error.

Choose your path

Both paths produce the same end state: a new image version running on the device. They differ in who pulls the trigger and how traceable the rollout is. Most teams start with the manual path and graduate to the automated path once releases settle into a cadence.

Path A

Manual update

Build locally, push to cr.sixfab.io, then click Restart or Edit Deployment on each device from ALPON Cloud.

  • Best for small fleets and early-stage projects
  • Zero CI/CD setup required
  • Per-device control over rollout timing
  • Works with any image tag strategy
Trigger: dashboard click
Path B

Automated update

CI pipeline builds for linux/arm64, pushes a new tag, then calls the Sixfab Developer API to restart the deployment across the fleet.

  • Best for production fleets at scale
  • Reproducible builds tied to git commits
  • Fleet-wide rollout from a single push
  • Pairs with immutable tags for traceability
Trigger: git tag or merge

Prerequisites

Before either update path, make sure your build environment, registry account, and target devices are ready.

What you need
Build host Workstation or CI runner with Docker 24+ and Docker Buildx
Target architecture linux/arm64 (required for ALPON X5 AI and ALPON X4)
Sixfab account Active ALPON Cloud account with at least one provisioned ALPON device
Registry credentials 6-digit registry username and password set from Developer Tools → Registry in ALPON Cloud
Existing deployment A container already running on the device whose image you want to update
Network access Outbound HTTPS to cr.sixfab.io from the build host (cellular, Wi-Fi, or wired)
For automated path Sixfab Developer API key with asset management scope, plus CI runner secrets
No per-device docker login needed

Every ALPON ships preconfigured with credentials scoped to its owner's registry namespace. Devices auto-authenticate against cr.sixfab.io and pull private images without any per-device credential setup. You only run docker login on your build host, not on the device.


Dockerfile templates for ALPON

Every update starts with a rebuilt image. Use these production-tested Dockerfile templates as a starting point. All templates use slim or alpine base images, install only runtime dependencies, run as a non-root user, and produce small layer caches that push quickly over cellular links.

Common rules across stacks

  • Always pin the base image to a specific minor version (for example, python:3.12-slim instead of python:slim).
  • Use multi-stage builds for compiled languages (Go, Rust, Node TypeScript) to keep runtime images small.
  • Copy requirements.txt, package.json, or go.mod before the rest of the source so Docker can cache dependency layers.
  • Set WORKDIR /app and run the process as a non-root USER when possible.
  • Define EXPOSE with the container-side port, not the host port. Host ports are configured at deploy time in ALPON Cloud and must fall in the 30000-32767 range.

Pick a starting point

Python application

For a Python service such as an MQTT publisher, REST API, or data collector. The template installs system dependencies in a single layer, then installs Python packages in a separate cached layer.

Dockerfile (Python)
FROM python:3.12-slim AS base

ENV PYTHONDONTWRITEBYTECODE=1     PYTHONUNBUFFERED=1     PIP_NO_CACHE_DIR=1

WORKDIR /app

# System packages (kept minimal for cellular pull speed)
RUN apt-get update && apt-get install --no-install-recommends -y       ca-certificates curl     && rm -rf /var/lib/apt/lists/*

# Dependency layer (cached unless requirements.txt changes)
COPY requirements.txt ./
RUN pip install -r requirements.txt

# Application code
COPY . .

USER 1000:1000
EXPOSE 8080
CMD ["python", "-u", "main.py"]

Node.js application

For a Node.js HTTP service or worker. The multi-stage build separates dependency installation from the runtime image, producing a slimmer final layer.

Dockerfile (Node.js)
# Stage 1 - install dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev

# Stage 2 - runtime
FROM node:20-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

USER node
EXPOSE 3000
CMD ["node", "server.js"]

Go application

For a compiled Go binary. The build stage compiles statically for ARM64, then the runtime stage uses scratch or alpine for a minimal image, often under 20 MB.

Dockerfile (Go)
# Stage 1 - build for ARM64
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm64
RUN go build -ldflags="-s -w" -o /out/app ./cmd/app

# Stage 2 - minimal runtime
FROM alpine:3.20
RUN apk add --no-cache ca-certificates
COPY --from=build /out/app /usr/local/bin/app
USER 1000:1000
EXPOSE 8080
CMD ["/usr/local/bin/app"]

Static site or Nginx reverse proxy

For a static frontend, dashboard, or thin Nginx layer in front of another container. Useful as a quick smoke test image too.

Dockerfile (Nginx)
FROM nginx:1.27-alpine

# Static assets
COPY ./dist /usr/share/nginx/html

# Optional custom server block
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Layer caching means faster updates

Docker only transfers layers that have changed since the last push. If your dependency layer is unchanged, a code-only update over cellular can transfer in under 30 seconds. Keep dependency installation in a separate, early layer to maximize cache hits across releases.


Tagging strategy

How you tag releases determines how you update them. The two patterns below cover almost every team's needs. You can also use both at once: ship every release with both an immutable tag and the :latest alias.

Tag pattern comparison
Mutable tag :latest, :stable, :prod. The tag points to a moving target. Updating means pushing a new image under the same name, then restarting the container so it pulls the new digest.
Immutable tag :v1.2.3, :2026-05-22, :<git-sha>. Each release gets a unique tag that never changes. Updating means pushing a new tag and reconfiguring the deployment to point at it.
Hybrid Tag every release with both :v1.2.3 and :latest. Use the immutable tag for production deployments; use :latest for dev and one-tap restart updates.
Recommended For production fleets: immutable tags. Rollback is a tag change, audit trail is preserved per device, and dependent assets cannot accidentally drift to a broken build.
Registry deletion rule The platform will not allow deletion of any image or tag currently in use by a device. Use See Usage on the Registry page to identify dependent assets before cleanup.
Prefer immutable tags in production

Mutable tags like :latest make rollback impossible if a regression slips in. Tag every release with an immutable identifier (semver, git SHA, or build number) and update deployments explicitly.


Manual update from the dashboard

The fastest path. You build and push from your workstation, then drive the rollout from ALPON Cloud with two clicks. Use this path for small fleets, staging environments, or one-off urgent fixes.

  1. 1

    Authenticate to the registry

    From your build host, log in to cr.sixfab.io with the 6-digit username from the Registry page in ALPON Cloud. If you have not set a password yet, click Reset Password on that page and save the displayed value (it is shown once).

    Sixfab Container Registry page header showing the 6-digit registry username at the top of the page
    Your 6-digit registry username appears at the top of the Registry page in ALPON Cloud.
    bash
    docker login cr.sixfab.io --username <your_username>
    # Password: paste the registry password (hidden input)
  2. 2

    Rebuild for ARM64 and tag

    Build the image with Docker Buildx, targeting linux/arm64, then tag it with the full registry path. Pick the new tag according to your tagging strategy.

    bash
    # 1. Build for ARM64 from the Dockerfile directory
    docker buildx build --load --platform linux/arm64   -t my-app:v1.2.3 .
    
    # 2. Tag with the registry path
    docker tag my-app:v1.2.3 cr.sixfab.io/<your_username>/my-app:v1.2.3
  3. 3

    Push the new image

    Upload the image to your private repository. Layer caching means only changed layers are transferred. Refresh the Registry page in ALPON Cloud to confirm the new tag, size, and digest appear.

    bash
    docker push cr.sixfab.io/<your_username>/my-app:v1.2.3
    
    # The push refers to repository [cr.sixfab.io/719663/my-app]
    571fd9f78ac9: Pushed
    v1.2.3: digest: sha256:2aeab3830f8eaf2775fd599fda6a516a95c1480239ba5e64a632a68c1f3bd53d
    size: 31.07 MB
    Sixfab Container Registry page on Connect showing the list of pushed repositories with name, total size, created date, and last update timestamp
    Registry page lists every repository with size, creation date, and last-update timestamp.
  4. 4

    Roll the update out to the device

    Open the device's Assets → Applications tab in ALPON Cloud. Pick the action that matches your tagging strategy.

    If you re-pushed a mutable tag (:latest)

    Click Restart next to the running container. The device stops the container, pulls the latest digest from cr.sixfab.io, and starts a fresh instance. State held in mounted volumes is preserved; state in the container filesystem is reset.

    If you pushed a new immutable tag (:v1.2.3)

    Click Edit Deployment, change the image tag in the form, and save. The new tag is pulled and the container is recreated. Environment variables, ports, and volume mounts are preserved unless you change them in the form.

  5. 5

    Verify the new version is running

    Watch the status flip back to Running in the Applications list. Then open Logs to confirm startup messages from the new version, or open the Container Terminal and inspect a version file or environment variable that you bumped with the release.

    Container Registry image details page showing repository, tag, digest, architecture arm64, and manifest layers
    Click Details on a repository to inspect each tag with its digest, architecture (must read arm64), and manifest layers. Use See Usage to identify which devices run it.

    See Verify the rollout for the full post-update checklist.


Automated update from CI/CD

The Sixfab Container Registry is a standard OCI registry, so any CI system that can run Docker can build and push to it. To turn a CI push into a live rollout, pair the push step with a call to the Sixfab Developer API that triggers a restart on every device running the deployment.

GitHub Actions: build, push, and trigger restart

Store the following values as repository secrets before running the workflow:

  • SIXFAB_REGISTRY_USERNAME: your 6-digit registry username
  • SIXFAB_REGISTRY_PASSWORD: your registry password
  • SIXFAB_API_KEY: a Developer API key with asset management scope
  • ASSET_ID: the ID of the device (or asset group) to update
.github/workflows/release.yml
name: Build, push, and roll out to ALPON

on:
  push:
    tags: ['v*']

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Sixfab Registry
        uses: docker/login-action@v3
        with:
          registry: cr.sixfab.io
          username: ${{ secrets.SIXFAB_REGISTRY_USERNAME }}
          password: ${{ secrets.SIXFAB_REGISTRY_PASSWORD }}

      - name: Build and push (linux/arm64)
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/arm64
          push: true
          tags: |
            cr.sixfab.io/${{ secrets.SIXFAB_REGISTRY_USERNAME }}/my-app:${{ github.ref_name }}
            cr.sixfab.io/${{ secrets.SIXFAB_REGISTRY_USERNAME }}/my-app:latest

      - name: Trigger restart on ALPON device
        env:
          SIXFAB_API_KEY: ${{ secrets.SIXFAB_API_KEY }}
          ASSET_ID: ${{ secrets.ASSET_ID }}
        run: |
          curl -X POST "https://api.sixfab.com/v1/assets/$ASSET_ID/applications/my-app/restart"             -H "Authorization: Bearer $SIXFAB_API_KEY"             -H "Content-Type: application/json"

GitLab CI equivalent

.gitlab-ci.yml
build_and_release:
  image: docker:24
  services: [docker:24-dind]
  variables:
    DOCKER_BUILDKIT: "1"
  only: [tags]
  before_script:
    - echo "$SIXFAB_REGISTRY_PASSWORD" | docker login cr.sixfab.io         -u "$SIXFAB_REGISTRY_USERNAME" --password-stdin
  script:
    - docker buildx create --use
    - docker buildx build --platform linux/arm64         -t cr.sixfab.io/$SIXFAB_REGISTRY_USERNAME/my-app:$CI_COMMIT_TAG         -t cr.sixfab.io/$SIXFAB_REGISTRY_USERNAME/my-app:latest         --push .
    - apk add --no-cache curl
    - curl -X POST "https://api.sixfab.com/v1/assets/$ASSET_ID/applications/my-app/restart"         -H "Authorization: Bearer $SIXFAB_API_KEY"         -H "Content-Type: application/json"

Two automation patterns

Pushing a new tag does not automatically redeploy a running container. You decide how the trigger fires.

Automation pattern comparison
Pattern A: :latest + Restart CI overwrites :latest on every release, then calls the API to restart the deployment. The device pulls the new digest under the same tag. Fast, simple, but sacrifices traceability per device.
Pattern B: immutable tag + Edit Deployment CI pushes a new :v1.2.3 tag, then calls the API to update the deployment to that tag. The audit log shows the exact version each device runs. Recommended for production.
Fleet-wide rollout Iterate over a list of asset IDs in the CI script, or call a ALPON Cloud asset group endpoint to fan out one request to many devices.
Staged rollouts Split asset IDs into rings (canary, beta, production). Push to canary first, wait for a smoke-test job to pass, then release to wider rings.
Endpoint paths are illustrative

The exact API path for restart and edit-deployment operations is described in the Sixfab Developer API reference. The examples above show the shape of the call; check the API reference for the current endpoint and request schema before deploying CI.


Rollback

Rollback is only as easy as your tagging strategy permits. With immutable tags, you point the deployment at the previous tag and click save. With mutable tags, rollback is impossible unless you kept the previous image digest somewhere accessible.

Rollback procedures
Immutable tag rollback Click Edit Deployment, change the image tag from :v1.2.3 back to :v1.2.2, save. The device pulls the older image (it is usually still cached locally) and starts it. Completes in seconds.
Mutable tag rollback Re-push the previous image source under :latest from your build host, then click Restart. Only possible if your source tree is git-versioned and you can check out the previous release.
CI-driven rollback Re-run the release job for the previous git tag. Because the build is reproducible, the same digest is republished. Pair with an API call to restart the deployment.
Multi-device rollback Use the same fleet-wide pattern as forward rollout: iterate asset IDs, or call an asset-group endpoint that fans out the update to every device in the ring.
Volume data Mounted volumes survive a rollback unchanged. If your previous version is incompatible with new data schema (for example, a database migration was applied), restore the volume from backup before rolling back.
Do not delete the previous tag

Even after a successful release, keep the previous N tags in the registry. The platform blocks deletion of tags currently in use by a device, but a manual cleanup that runs before rollout completes can still leave you without a rollback target. A safe retention policy is to keep the last 5 to 10 immutable tags.


Verify the rollout

The container status flipping back to Running is necessary but not sufficient. Run through the checks below before declaring the rollout complete.

Post-update checklist
Container status Status is Running in the Applications list, with the new image tag visible.
Registry usage On the Registry page, click See Usage on the new tag. The device is listed.
Startup logs Click Logs next to the container. The view tails the last 1000 lines and streams new output. Confirm startup messages from the new version.
Health endpoint If your service exposes a health endpoint, hit it via the mapped host port (for example, http://<device-ip>:30800/health).
Resource usage Open View Status to confirm CPU, memory, and network sit at expected baselines.
Version stamp Open the Container Terminal and run cat /app/VERSION or print an environment variable that you bumped with the release.

Troubleshooting

Common failure modes when updating a container on ALPON, and how to recover from each.

Image Container fails to start with exec format error after update

Symptom

The container is created but exits immediately. The Logs panel shows:

container logs
exec /usr/local/bin/app: exec format error

Cause

The new image was built for linux/amd64 instead of linux/arm64. ALPON X5 AI (Raspberry Pi CM5) and ALPON X4 (Raspberry Pi CM4) cannot execute x86 binaries.

Fix

Rebuild with the correct platform flag and verify the manifest before re-pushing:

bash
# 1. Rebuild explicitly for ARM64
docker buildx build --load --platform linux/arm64   -t my-app:v1.2.3 .

# 2. Verify the architecture field reads "arm64"
docker manifest inspect cr.sixfab.io/<your_username>/my-app:v1.2.3

In CI, double-check the platforms: line in your workflow.

Auth unauthorized: authentication required when pushing the update

Symptom

docker push rejects the upload before the first layer is transferred:

terminal · error
unauthorized: authentication required

Cause

The local Docker session is not authenticated against cr.sixfab.io, or the registry password has been reset since the last successful login.

Fix

Re-authenticate the build host. If the password is lost, click Reset Password on the Registry page in ALPON Cloud to issue a new one and store it in a secrets manager (it is shown only once).

bash
docker login cr.sixfab.io --username <your_username>
Rollout Device still runs the old version after a Restart

Symptom

You pushed a new image, clicked Restart, the container status flipped back to Running, but the version stamp or behavior is unchanged.

Cause

You pushed under a new tag (for example, :v1.2.3), but the deployment still references the old tag. Restart re-pulls the configured tag; it does not switch tags.

Fix

Click Edit Deployment instead of Restart. Change the image tag in the form to your new tag, then save. The new image is pulled and the container is recreated with the new tag.

Network Update pull is very slow over cellular

Symptom

The container stays in Pulling state for several minutes on a device with a cellular uplink. Subsequent restarts of the same tag are fast, but each new tag is slow again.

Cause

Initial pulls of large images over cellular are slow. If you also changed dependency layers (not just code), the entire dependency layer must transfer instead of just the application layer.

Fix

Restructure the Dockerfile so the dependency install layer is built before the application code copy. Docker caches that layer; subsequent code-only updates transfer in seconds:

Dockerfile · correct order
COPY requirements.txt ./
RUN pip install -r requirements.txt

# Code goes last so it stays its own thin layer
COPY . .
Config port is already allocated after Edit Deployment

Symptom

The deployment save fails immediately and the Logs panel shows a port binding error:

deployment · error
Error: Bind for 0.0.0.0:30800 failed: port is already allocated

Cause

You changed the host port in the Edit Deployment form to one that another container on the same device is already using. Each host port can only be bound to one container at a time.

Fix

Pick a different host port from the 30000-32767 range and save again. The container-side port stays the same; only the host mapping changes.

Runtime Container restart-loops after update

Symptom

The Applications list flips between Running and Exited every few seconds. Logs show the container crashing on startup.

Cause

The new version crashes during initialization. The three usual culprits: a missing environment variable, a schema mismatch with mounted volume data (a migration was not applied), or an unhandled exception in the new startup code.

Fix

Open Logs and read the crash trace. Address based on what you find:

  • Missing env var: add it via Edit Deployment, then save.
  • Volume data mismatch: stop the container, run the migration, restart.
  • New-code regression: roll back to the previous tag using the procedure in Rollback while you debug.
Registry Cannot delete an old tag from the Registry

Symptom

Clicking Delete on an old tag or repository in the Registry page shows an error and the action is blocked.

Cause

The platform blocks deletion of any image or tag currently in use by a device, to prevent system errors on active deployments.

Fix

Click See Usage next to the tag. Update each device listed to a newer tag using the manual or automated path. Once no device uses the old tag, deletion is allowed.