Skip to content

Platforms

martin edited this page Aug 16, 2026 · 6 revisions

Platforms

The same image name works everywhere, but each host has one thing you must get right. These are the differences that actually break setups.

At a glance

Platform Base image Timezone Mount owner Watch out for
linux/amd64 ASP.NET runtime (Debian) TZ, IANA id uid 1654 Leave Controller__TimeZone empty
linux/arm64 ASP.NET runtime (Debian) TZ, IANA id uid 1654 Enable cgroup memory accounting first
nanoserver ltsc2022 ASP.NET runtime (Nano Server) Controller__TimeZone, Windows id n/a No ICU; TZ is ignored; amd64 only

Linux x64 — linux/amd64

The straightforward case — nothing platform-specific beyond the uid 1654 ownership covered in Configuration. The command in Docker works as written.

Raspberry Pi — linux/arm64

Raspberry Pi OS ships with cgroup memory accounting disabled — the firmware prepends cgroup_disable=memory to the kernel command line, so it appears in /proc/cmdline while no file on the boot partition mentions it. Without it every mem_limit is silently ignored: docker inspect records Memory=0 and docker stats shows 0B / 0B for every container. On a 1 GB board that is the difference between one container being killed and the whole box thrashing; on a 4 GB one you mainly lose the per-container figures you need to diagnose a leak. Required on every board whatever its memory — what you append below lands after the firmware's copy, and the later parameter wins.

sudo cp /boot/firmware/cmdline.txt /boot/firmware/cmdline.txt.bak

# cmdline.txt is ONE single line. The bootloader reads only the first line,
# so appending a second line leaves the parameters completely inert.
sudo sh -c '
  line=$(tr "\n" " " < /boot/firmware/cmdline.txt | tr -s " " | sed "s/[[:space:]]*$//")
  case $line in
    *cgroup_enable=memory*) ;;
    *) line="$line cgroup_enable=memory cgroup_memory=1" ;;
  esac
  printf "%s\n" "$line" > /boot/firmware/cmdline.txt
'

wc -l /boot/firmware/cmdline.txt   # MUST print 1
cat  /boot/firmware/cmdline.txt    # check it before rebooting
sudo reboot

Check the file before rebooting: a mangled cmdline.txt means a Pi that will not boot, and fixing that needs the storage in another machine. After the reboot, confirm what the kernel actually booted with — that is the authoritative answer.

cat /proc/cmdline | tr ' ' '\n' | grep cgroup
docker info 2>&1 | grep -i "memory limit"   # note 2>&1 — warnings go to stderr

Note

Two things that look wrong but aren't. cgroup_disable=memory stays visible in /proc/cmdline — the Pi firmware injects it and it exists in no file you can edit. Firmware parameters come first, so your cgroup_enable=memory is processed later and wins. Both being present is the normal working state. Separately, WARNING: No swap limit support is expected and harmless — swap accounting is a different facility and nothing here asks for it. Only No memory limit support matters.

On Bullseye and older the file is /boot/cmdline.txt instead — /boot/firmware/ is the Bookworm-and-later layout, where /boot/cmdline.txt is a stub that says not to edit it.

Windows — nanoserver ltsc2022

The Windows image is built on Nano Server ltsc2022, which runs on both Windows Server 2022 and 2025 hosts. Your Docker daemon must be in Windows containers mode.

Warning

You must set the timezone explicitly. .NET on Windows ignores the TZ environment variable entirely, so an unconfigured container runs in UTC and stamps every recorded charging session with the wrong day. Nano Server also ships no ICU, so the IANA-to-Windows zone mapping is unavailable — you must give a Windows zone id, not an IANA one.

docker run -d --name solax-controller --restart on-failure ^
  -e Controller__TimeZone="Central Europe Standard Time" ^
  -e Solax__Inverter__Host=192.168.2.10 ^
  -e Solax__EvCharger__Host=192.168.2.6 ^
  -v C:\solax\data:C:\app\data ^
  -v C:\solax\logs:C:\app\logs ^
  ghcr.io/mpospisil/solax-controller:latest

Use Central Europe Standard Time, not Europe/Prague. An id that cannot be resolved stops the worker at startup rather than quietly reverting to UTC — that failure is deliberate, and it is the good outcome.

Building locally, if you must

The published images are cross-compiled rather than emulated, so no QEMU is involved. You only need this to test an unreleased change.

docker build --platform linux/arm64 -t solax-controller .
docker build --platform linux/amd64 -t solax-controller .

# Windows needs its own Dockerfile — a Dockerfile targets one OS,
# and a Windows runtime stage only assembles on a Windows daemon.
docker build -f Dockerfile.windows -t solax-controller:nanoserver .

Previous: Docker · Next: Deployment

SolaX Local Controller

Advanced


Images: ghcr.io/mpospisil/solax-controller

Clone this wiki locally