Building for Production

Photonix supports multiple architectures (amd64, arm32v7, arm64v8). We use Docker to build the server-side app image which provides a consistent runtime and can be run on Linux, Windows or MacOS. Building an amd64 image on a amd64 machine is simple enough and can be done with the standard Docker installation. For other architectures, we cross-compile using an experimental Docker extension called buildx.

Automated builds

Docker images for all supported architectures get built automatically via GitHub Actions and Docker buildx. New releases can be created by tagging the latest commit on the master branch and this will trigger a new build.

git tag v1.0.0
git push --tags

Pushed images end up on our Docker Hub page tagged as latest and parts of the version number - in this example, 1.0.0, 1.0 and 1. You can view existing tags from the repo to determine the next version with git tag.

More details about the buildx process can be read further down in the Cross-compiled Docker Images.

Native Architecture Docker Image

From the cloned respository you should be able to run the following to build for the current architecture:

make build-prd

To run the production image run the following:

make start-prd

Cross-compiled Docker Images

If you want to compile for an architecture different to what you are running (e.g. compiling for ARM on an x86 machine), you'll need to have the Docker buildx extension installed and bootstrapped for cross-compilation. The following are simplified instructions but you can find the source material here.

Setting up Docker buildx

First we turn on experimental features in Docker daemon. Edit the file /etc/docker/daemon.json and add the following.

  "experimental": true

Restart the server.

service docker restart

Now we enable experimental features in the client. Edit ~/.docker/config.json and add this inside the top-level object:

"experimental": "enabled"

We then need to download and install the buildx plugin. You may want to pick a newer version but this is the latest at time of writing.

mkdir -p ~/.docker/cli-plugins/
curl -L -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx

Run this to check whether the plugin is installed correctly:

docker help | grep buildx

You should expect to see something like this:

buildx*     Build with BuildKit (Docker Inc., v0.5.1)

You'll need to create a builder container for buildx:

docker buildx create --name my-new-builder --driver docker-container --use

At this point buildx will only support your current architecture. To bootstrap support for all architectures run this:

docker buildx inspect --bootstrap

You should see a list of supported platforms at the end of the above command.

Building Docker images

You can then start building for your desired architecture, setting --platform in the command below to your desired architecture. Currently linux/amd64, linux/arm64 and linux/arm/v7 are supported.

docker buildx build --platform linux/amd64,linux/arm/v7,linux/arm64 --tag photonixapp/photonix --push -f docker/Dockerfile.prd .

Buildx uses a different storage backed to standard Docker so build images will not display when running the docker images command and cannot be easily run. For this reason it is recommended to be logged in with docker login and push at the same time (--push in the shown command). You can change the tag to upload to your personal Docker Hub account by changing --tag YOUR_USERNAME/photonix in the command above.

Upgrading and caching Python packages

For users upgrading Python dependencies, some packages with C extensions (e.g. Numpy and Matplotlib) will need compiling for ARM architectures which takes a long time. These packages can be automatically uploaded to our custom PyPI server to speed up future builds. You'll need write access to the PyPI server and specify authentication credentials with --build-ags as shown below.

docker buildx build --platform linux/amd64,linux/arm/v7,linux/arm64 --tag photonixapp/photonix --push -f docker/Dockerfile.prd --build-arg PYPI_UPLOAD_USERNAME=YOUR_USERNAME --build-arg PYPI_UPLOAD_PASSWORD=YOUR_PASSWORD .

Potential errors

It might be due to the fact that buildx is experimental or that some base images differ between architectures but we found several inconsistencies. Builds would fail at a certain point on one architecture but be fine in another. Building directly on a Raspberry Pi would work successfully when cross-compiling for it wouldn't (even when using buildx on the Pi).

It can be a very fiddly job to get all architectures working - getting buildx working for all architectures initially took solid weeks of work spread across many months.

You may need so switch some commands for slightly different ones install extra packages. It can also be useful to break down multi-line docker RUN commands to find errors and speed up iterations. It's also good to have a real device (like a Raspberry Pi) SSH'd into to confirm running the same command there produces the same error.