I have been an avid user of the MagicMirror² framework for almost 6 years now. I have developed, and look after a range of modules. This document will detail these modules and their usage.

🧱 Features

  • Based on debian:trixie for Pi 5 compatibility
  • Installs MagicMirror² v2.32+
  • Allows custom modules + volume persistence
  • Exposed port for local display

🧪 Custom Modules

🪞 MagicMirror² in Docker — My Complete Setup

  • I recently set up MagicMirror² to run fully containerized in Docker with support for:
  • Mounted custom modules, config.js, and custom.css
  • Server-only mode for remote/browser display
  • Clean build → test → push to Docker Hub workflow

Here’s how I did it.

1️⃣ Prepare the Project Structure

Create a working folder for your MagicMirror container:

mkdir magicmirror-docker
cd magicmirror-docker

mkdir config custom-css my-modules
touch config/config.js
touch custom-css/custom.css
  • config/ → contains config.js
  • custom-css/ → contains custom.css
  • my-modules/ → where all your custom modules go

2️⃣ Create the Dockerfile

FROM debian:trixie-20250520

SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]
ENV DEBIAN_FRONTEND=noninteractive

# Create non-root user
RUN useradd -ms /bin/bash pi
USER root

# Install system deps
RUN apt-get update && apt-get -y upgrade && apt-get install -y --no-install-recommends \
  curl git nano procps tini ca-certificates xz-utils python3 gnupg build-essential \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

# Dummy sudo for some npm postinstall scripts
RUN echo -e '#!/bin/bash\nexec "$@"' > /usr/local/bin/sudo && chmod +x /usr/local/bin/sudo

# Install Node.js 22.x
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
    apt-get install -y nodejs && node -v && npm -v

# Switch to non-root
USER pi
WORKDIR /home/pi

# Clone MagicMirror and install dependencies
RUN git clone https://github.com/MagicMirrorOrg/MagicMirror.git
WORKDIR /home/pi/MagicMirror
RUN npm install && npm run install-mm

# Copy entrypoint script
COPY entrypoint.sh /home/pi/entrypoint.sh
RUN chmod +x /home/pi/entrypoint.sh

EXPOSE 8080
ENTRYPOINT ["/home/pi/entrypoint.sh"]

3️⃣ Create the Entrypoint Script

entrypoint.sh handles linking modules and loading config/css:

#!/bin/bash
set -euxo pipefail

MM_DIR="/home/pi/MagicMirror"
MODULES_DIR="$MM_DIR/modules"
CUSTOM_MODULES_DIR="$MM_DIR/custom-modules"
CUSTOM_CONFIG="/home/pi/MM-config/config.js"
CUSTOM_CSS="/home/pi/MM-css/custom.css"

# Load config.js if provided
if [ -f "$CUSTOM_CONFIG" ]; then
  cp "$CUSTOM_CONFIG" "$MM_DIR/config/config.js"
  echo "✅ Loaded custom config.js"
fi

# Load custom.css if provided
if [ -f "$CUSTOM_CSS" ]; then
  cp "$CUSTOM_CSS" "$MM_DIR/css/custom.css"
  echo "✅ Loaded custom.css"
fi

# Link custom modules
if [ -d "$CUSTOM_MODULES_DIR" ]; then
  for dir in "$CUSTOM_MODULES_DIR"/*; do
    [ -d "$dir" ] || continue
    name=$(basename "$dir")
    target="$MODULES_DIR/$name"
    if [ ! -L "$target" ]; then
      ln -s "$dir" "$target"
      echo "✅ Linked custom module: $name"
    fi
  done
fi

# Start MagicMirror in server-only mode
cd "$MM_DIR"
exec npm run server

4️⃣ Docker Compose Setup

docker-compose.yml mounts your local folders into the container:

version: "3.8"
services:
  magicmirror:
    image: yourusername/magicmirror:latest
    container_name: magicmirror
    ports:
      - "8080:8080"
    volumes:
      - ./config:/home/pi/MM-config
      - ./custom-css:/home/pi/MM-css
      - ./my-modules:/home/pi/MagicMirror/custom-modules
    restart: unless-stopped

5️⃣ Build, Test, and Run Locally

docker-compose build --no-cache
docker-compose up -d
docker logs -f magicmirror

Visit:

http://localhost:8080

Notes:

  • If you are doing these in a Windows environment, you need to have docker-desktop installed.
  • You may also want to do the above steps in a WSL environment

6️⃣ Scripts for Automation

build-local.sh (Test Locally)

#!/bin/bash
set -e
docker-compose build --no-cache
docker-compose up -d

build-and-push.sh (Push to Docker Hub)

#!/bin/bash
set -e
DOCKER_USERNAME="yourusername"
IMAGE_NAME="magicmirror"

docker build -t $DOCKER_USERNAME/$IMAGE_NAME:latest .
docker push $DOCKER_USERNAME/$IMAGE_NAME:latest

Make them executable:

chmod +x build-local.sh build-and-push.sh

7️⃣ Browser Display and Portrait Mode

  • MagicMirror is running in server-only mode.
  • Rotate the display on the viewing device (tablet, monitor, or laptop).
  • On Raspberry Pi with a local screen, use /boot/config.txt to set display_rotate=1

✅ Final Notes

  • All custom modules, config, and CSS live outside the container for easy updates.
  • Core MagicMirror and default modules are baked into the image.
  • Upgrading is as simple as:
docker-compose down
docker pull yourusername/magicmirror:latest
docker-compose up -d

This setup gives you a clean, maintainable MagicMirror Docker workflow, from local testing to pushing your own Docker Hub image.

Some screenshots of the modules under my guard

Growatt MMM-Growatt

Growatt-Stats MMM-Growatt-Stats

EskomSePush MMM-EskomSePush

Gas Monitor MMM-GasMonitor

MovieListing MMM-MovieListing

NewsAPI MMM-NewsAPI

NOAA3 MMM-NOAA3

PhilipsHue MMM-PhilipsHue

Reddit MMM-Reddit

Rugby MMM-Rugby

SweepClock MMM-SweepClock

WOTD MMM-WOTD