While developing a web app for my Raspberry Pi Zero SMS gateway I ran into trouble. I wanted to be able to run the app in Docker but building an image from a Dockerfile on the RPi0 was incredibly slow and I didn't manage to complete it because my Pi ran out of memory.

I thought about creating the image locally and pushing it to Docker Hub but it felt a bit overkill for small, personal apps. After some thinking and googling I realised that you can save docker images and then load them on another computer.

There are 2 ways to go about this:

  1. Install npm on your computer, fetch all dependencies (node_modules) and copy them together with the code into the armv6 image.
  2. Run npm in a container with a multistage build and then copy the files to image.

With local NPM

This is more useful and faster with an app you're developing yourself, since you probably have npm installed.

Fetch the deps as usual with npm install then add a Dockerfile (and possibly a .dockerignore).

# Check for your version: https://hub.docker.com/r/arm32v6/node/tags/
FROM arm32v6/node:8.14.0-alpine
RUN mkdir /app
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH

# Either be specific with what to add, docker caches every step
ADD package.json /app/package.json
ADD package-lock.json /app/package-lock.json
ADD node_modules /app/node_modules
ADD app.js /app/app.js

# Or add the whole dir, I always use a .dockerignore file when doing that
ADD . /app

ENV PORT=5000
EXPOSE 5000
CMD [ "npm", "start" ]

Multistage build

This is useful when you want to make an image of someone elses project and/or don't want to install npm on your machine.

Since you can't run the arm32v6/node on your computer, you need to first use a working node image to fetch/build your project.

# Fetch node_modules for backend, nothing here except 
# the node_modules dir ends up in the final image
FROM node:8.14.0-alpine as builder
RUN mkdir /app
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install

# Add the files to arm image
FROM arm32v6/node:8.14.0-alpine
RUN mkdir /app
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH

# Same as earlier, be specific or copy everything
ADD package.json /app/package.json
ADD package-lock.json /app/package-lock.json
ADD . /app

COPY --from=builder /app/node_modules /app/node_modules

ENV PORT=5000
EXPOSE 5000
CMD [ "npm", "start" ]

Building

Then you can build and save your image...

docker build -t my-app -f Dockerfile .
docker save -o my-app.tar my-app
ls
# my-app.tar

... transfer it to your RPi (with scp or whatever) and load it!

# On RPi
docker load -i my-app.tar
docker run --rm -p 5000:5000 my-app

Automation

Doing this can get tedious if you make a lot of changes to your app, so I created a simple bash script that you can use: https://github.com/joenas/node-docker-raspberry-zero

Note: In my repo I've named the file Dockerfile.armv6 because I already have a Dockerfile for running the app on other architectures that doesn't require a specific docker image. I also tag the image with the suffix :armv6. The commands above would then be like this:

# Build & save
docker build -t my-app:armv6 -f Dockerfile.armv6 .
docker save -o my-app.tar my-app:armv6

# Load & run
docker load -i my-app.tar
docker run --rm -p 5000:5000 my-app:armv6

Sample .dockerignore

Here's a sample .dockerignore file you can use to not include all the files in your image.

/.git/
.gitignore

docker-compose.yml
Dockerfile*
LICENSE
README.md
*.tar

Sharing is caring

I hope you have any use for this and I'd love to see more stuff for the RPi0/1 so please comment below if you create something! 🙏