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:
- Install
npm
on your computer, fetch all dependencies (node_modules
) and copy them together with the code into thearmv6
image. - 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! 🙏