MagicMirror Installation Guide - Raspberry Pi 4

Maintainer: Tony Di Bona | Updated: May 2, 2026

Prerequisites

Step 1 - Flash SD Card

Using Raspberry Pi Imager, select Raspberry Pi OS 64-bit (2026-04-21). Click the settings gear and configure:

Step 2 - First Boot Setup

sudo apt update && sudo apt upgrade -y

Step 3 - Disable Sudo Password

sudo visudo

Find this line: %sudo ALL=(ALL:ALL) ALL

Change it to:

%sudo   ALL=(ALL:ALL) NOPASSWD: ALL

Step 4 - Install Node.js 24

curl -sL https://deb.nodesource.com/setup_24.x | sudo bash -
sudo apt install -y nodejs

Step 5 - Install MagicMirror

cd ~
git clone https://github.com/MagicMirrorOrg/MagicMirror
cd MagicMirror
npm run install-mm
cp ~/MagicMirror/config/config.js.sample ~/MagicMirror/config/config.js

Step 6 - Configure MagicMirror Autostart

mkdir -p ~/.config/autostart
vi ~/.config/autostart/magicmirror.desktop

Add this content:

[Desktop Entry]
Type=Application
Name=MagicMirror
Exec=bash -c 'cd /home/pindari/MagicMirror && WAYLAND_DISPLAY=wayland-0 npm start'
X-GNOME-Autostart-enabled=true

Step 7 - Configure Portrait Display Rotation

vi ~/.config/autostart/randr.desktop

Add this content:

[Desktop Entry]
Type=Application
Name=Display Rotation
Exec=bash -c 'WAYLAND_DISPLAY=wayland-0 wlr-randr --output HDMI-A-1 --transform 90 >> /tmp/randr.log 2>&1'
X-GNOME-Autostart-enabled=true

Step 8 - Force 1920x1080 Resolution

The Raspberry Pi 4 may not correctly read the monitor's EDID data and fall back to a lower resolution. To force 1920x1080:

sudo vi /boot/firmware/cmdline.txt

Add video=HDMI-A-1:1920x1080@60 to the end of the existing single line, separated by a space. The file must remain a single line.

Step 9 - Reboot

sudo reboot

Step 10 - Verify Settings

Verify the display resolution and rotation:

WAYLAND_DISPLAY=wayland-0 wlr-randr

Confirm that the output shows 1920x1080 px as the current mode and Transform: 90.

Verify Node.js and MagicMirror status:

node -v && npm -v
ps aux | grep electron | grep -v grep

Step 11 - Install MMM-ImageSlideshow

cd ~/MagicMirror/modules
git clone https://github.com/AdamMoses-GitHub/MMM-ImageSlideshow.git

Step 12 - Create Photos Folder

The photos folder already exists as exampleImages inside the module. Upload photos to:

~/MagicMirror/modules/MMM-ImageSlideshow/exampleImages/

Photos should be 1040 x 614 pixels and in 16:9 aspect ratio for best results.

Step 13 - Install MMM-Todoist2

cd ~/MagicMirror/modules
git clone https://github.com/ZachR19/MMM-Todoist2.git
cd MMM-Todoist2
npm install

Step 14 - Get Todoist API Token

  1. Go to todoist.com and log in
  2. Click your profile picture → SettingsIntegrationsDeveloper
  3. Copy your API token
  4. Note your Project ID from the URL when viewing your project: https://app.todoist.com/app/project/myProject-PROJECTID

Step 15 - Configure config.js

vi ~/MagicMirror/config/config.js

Replace the entire file with:

let config = {
    address: "localhost",
    port: 8080,
    basePath: "/",
    ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
    useHttps: false,
    httpsPrivateKey: "",
    httpsCertificate: "",
    language: "en",
    locale: "en-US",
    logLevel: ["INFO", "LOG", "WARN", "ERROR"],
    timeFormat: 24,
    units: "metric",
    modules: [
        {
            module: "clock",
            position: "top_left",
            header: "Local Time",
            config: {
                timezone: "Pacific/Auckland",
                timeFormat: 12,
                showPeriod: true,
                showDate: true,
                dateFormat: "ddd, MMM D, YYYY",
                displaySeconds: false,
            }
        },
        {
            module: "clock",
            position: "top_right",
            header: "California Time",
            config: {
                timezone: "America/Los_Angeles",
                timeFormat: 12,
                showPeriod: true,
                showDate: true,
                dateFormat: "ddd, MMM D, YYYY",
                displaySeconds: false,
            }
        },
        {
            module: "MMM-ImageSlideshow",
            position: "upper_third",
            config: {
                imagePaths: ["modules/MMM-ImageSlideshow/exampleImages/"],
                slideshowSpeed: 5000,
                randomizeImageOrder: true,
                fixedImageWidth: 1040,
                fixedImageHeight: 614,
            }
        },
        {
            module: "weather",
            position: "middle_center",
            config: {
                weatherProvider: "openmeteo",
                type: "forecast",
                lat: -41.2190,
                lon: 175.4601,
                maxNumberOfDays: 7,
                units: "metric",
                showPrecipitationAmount: true,
                showPrecipitationProbability: true,
                showWindDirection: true,
                roundTemp: true,
                colored: true,
                appendLocationNameToHeader: false,
            }
        },
        {
            module: "calendar",
            position: "bottom_left",
            header: "Upcoming Events",
            config: {
                maximumEntries: 10,
                maximumNumberOfDays: 30,
                showLocation: false,
                fade: false,
                timeFormat: "dateheaders",
                appendLocationNameToHeader: false,
                calendars: [
                    {
                        url: "YOUR_GOOGLE_ICAL_URL",
                        symbol: "calendar-days",
                    }
                ],
            }
        },
        {
            module: "MMM-Todoist2",
            position: "bottom_right",
            header: "Tasks",
            config: {
                accessToken: "YOUR_TODOIST_API_TOKEN",
                maximumEntries: 60,
                updateInterval: 10*60*1000,
                fade: false,
                showProject: false,
                sortOrder: ["priority"],
                projects: ["YOUR_PROJECT_ID"],
            }
        },
    ],
};
if (typeof module !== "undefined") { module.exports = config; }

Step 16 - Configure custom.css

vi ~/MagicMirror/config/custom.css

Add this content:

:root {
  --gap-body-left: 20px;
  --gap-body-right: 20px;
}

.clock .module-header {
  font-size: 1.65em;
}

.clock .date {
  font-size: 2.55em;
}

.clock .time {
  font-size: 4.65em;
}

.weather .forecast .day {
  font-size: 2em;
}

.weather table {
  font-size: 2em;
}

.region.upper.third {
  top: calc(33% - 60px);
}

.region.middle.center {
  top: calc(65% - 120px);
}

.region.bottom.left {
  max-height: 600px;
  overflow: hidden;
}

.region.bottom.right {
  max-height: 600px;
  overflow: hidden;
}

.calendar .module-header {
  font-size: 2em;
}

.calendar .title {
  font-size: 1.5em;
}

.calendar .time {
  font-size: 1.5em;
}

.calendar .symbol {
  font-size: 1em;
}

.calendar .dateheader {
  font-size: 1.5em;
}

.MMM-Todoist2 .module-header {
  font-size: 2em;
}

.MMM-Todoist2 .title {
  font-size: 1.5em;
}

.MMM-Todoist2 .dueDate {
  font-size: 1.5em;
}

.MMM-Todoist2 .projectname {
  font-size: 1.5em;
}

Step 17 - Get Google Calendar iCal URL

  1. Go to calendar.google.com on your computer
  2. Click the three dots next to the calendar you want to display
  3. Click Settings and sharing
  4. Scroll down to Integrate calendar
  5. Copy the Secret address in iCal format — it ends in basic.ics
  6. Edit config.js and replace YOUR_GOOGLE_ICAL_URL with this URL
vi ~/MagicMirror/config/config.js

Step 18 - Reboot

sudo reboot

Useful Tips

Layout capture

When connected via an SSH session, use the following steps to capture a screenshot of the MagicMirror display. This is useful for adjusting font sizes and panel positioning without having to physically view the mirror each time.

Install grim — a screenshot utility for Wayland:

sudo apt install grim

Capture the screen. The WAYLAND_DISPLAY environment variable must be set to target the correct Wayland session:

WAYLAND_DISPLAY=wayland-0 grim ~/screenshot.png

The screenshot is saved to your home directory as screenshot.png. Open an SFTP session on your desktop machine to download a copy for analysis.

Note: grim requires a running Wayland compositor. If MagicMirror has not yet started the Wayland session is not active and the command will return a "cannot connect to wayland display" error. Make sure MagicMirror is running before capturing.

Scheduling display on/off times

Use the following scripts combined with cron to automatically turn the monitor on and off on a daily schedule — useful for conserving power overnight without interrupting MagicMirror or any background processes.

Step 1 - Create the "Screen Off" script

Run this command in your terminal to create ~/screen_off.sh:

cat << 'EOF' > ~/screen_off.sh
#!/bin/bash
export XDG_RUNTIME_DIR=/run/user/1000
export WAYLAND_DISPLAY=wayland-0
/usr/bin/wlr-randr --output HDMI-A-1 --off
EOF

Step 2 - Create the "Screen On" script

Run this command in your terminal to create ~/screen_on.sh:

cat << 'EOF' > ~/screen_on.sh
#!/bin/bash
export XDG_RUNTIME_DIR=/run/user/1000
export WAYLAND_DISPLAY=wayland-0
/usr/bin/wlr-randr --output HDMI-A-1 --on
EOF

Step 3 - Make both scripts executable

chmod +x ~/screen_off.sh ~/screen_on.sh

Step 4 - Test the scripts manually

  1. Turn the screen off:
    ~/screen_off.sh

    (Your monitor should go black / enter standby immediately.)

  2. Turn the screen back on:
    ~/screen_on.sh

    (Your monitor should power back on and display MagicMirror.)

Step 5 - Automate on a daily schedule (cron)

  1. Open your user cron schedule editor:
    crontab -e

    (If prompted to select an editor, choose 1 for nano.)

  2. Add these two lines at the very bottom of the file (adjust hours to your preference):
    # Turn monitor OFF every night at 11:00 PM
    0 23 * * * /home/pindari/screen_off.sh
    
    # Turn monitor ON every morning at 7:00 AM
    0 7 * * * /home/pindari/screen_on.sh
  3. Save and close the editor (Ctrl + O, press Enter, then Ctrl + X).

Your Raspberry Pi will now automatically sleep and wake the monitor at your scheduled times without interrupting any background processes or corrupting the system.