apps.header.allAppsCount
MagicMirror
apps.detail.types.app from magicmirror's Repository
apps.detail.sections.overview
This template runs MagicMirror² in server-only mode, the correct mode for a headless Unraid server: the interface is served over HTTP and rendered in any browser or on a separate client device such as a Raspberry Pi. The electron scenario, which draws to a locally attached display, will not work on Unraid and is deliberately not offered.
No setup is required. Install it, open the WebUI, done. On first start the image writes a working config.js that listens on all interfaces and already permits the usual private network ranges, including 10.x, 172.16-31.x and 192.168.x. The template also runs the container as user 99:100 so that everything it writes into appdata is owned nobody:users like the rest of your Unraid shares.
That same config.js is where you enable modules and change the layout, at /mnt/user/appdata/magicmirror/config/config.js. Third party modules go into the modules directory. See https://docs.magicmirror.builders and https://modules.magicmirror.builders.
Security note. MagicMirror² has no built-in authentication. Anyone who can reach the port sees your calendar and whatever else you configure. Keep it on a trusted network and do not forward the port. Use a VPN or an authenticated HTTPS reverse proxy for remote access.
The container image is built and maintained by Karsten Hassel at https://gitlab.com/khassel/magicmirror. This template provides only the Unraid packaging.
Readme
View on GitHubMagicMirror² for Unraid
An Unraid Community Applications template for MagicMirror², the open source modular smart mirror platform.
This repository contains only the Unraid packaging. The container image it points
at — karsten13/magicmirror — is built
and maintained by Karsten Hassel. Nothing here
forks, rebuilds or modifies that image.
What this template does
It runs MagicMirror² in server-only mode. That is the correct mode for a headless
Unraid box: MagicMirror² serves its interface over HTTP, and you view it in a browser or
on a separate client device such as a Raspberry Pi. The electron scenario, which draws
to a locally attached display, will not work on Unraid and is deliberately not offered.
| Image | karsten13/magicmirror:wolfi-server |
| Port | 8080 |
| Config volume | /opt/magic_mirror/config |
| Modules volume | /opt/magic_mirror/modules |
| Extra Parameters | --user 99:100 |
Setup
There is none. Install from Community Applications, open the WebUI, done.
On first start the image writes a working config.js that listens on all interfaces and
already permits the private ranges you are likely on:
address: "0.0.0.0",
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1", "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8"],
Edit that file at /mnt/user/appdata/magicmirror/config/config.js to enable modules and
change the layout. ipWhitelist: [] allows every address if your network sits outside
those ranges.
Why --user 99:100 is in Extra Parameters
The image runs as UID 1000. Unraid's appdata belongs to 99:100 (nobody:users). Without
the override the container still starts — Unraid creates the host paths world-writable —
but every file it writes lands owned 1000:1000, which is wrong for an Unraid user share
and confuses the file manager and backup plugins. Leave the setting in place.
MagicMirror² has no authentication. Anyone who can reach the port sees your calendar and whatever else you configure. Keep it on a trusted network and do not forward the port — use a VPN or an authenticated HTTPS reverse proxy for remote access.
Verified on Unraid
Tested on a real server, not assumed. scripts/test-unraid-images.sh reproduces it.
| Unraid | 7.3.2 |
| Docker | 29.5.3, x86_64 |
| Images | wolfi-server, debian-server, alpine (all stable) |
| Result | HTTP 200 within seconds on all three; config.js, custom.css and basepath.js created and owned 99:100 |
Two details worth recording, because both look like problems until you check how Unraid actually behaves.
Unraid creates the host paths itself, and does it correctly. A plain docker run
against a non-existent bind-mount path lets Docker create it as root:root 0755, and the
container then cannot write. That is not what happens on install: Unraid's docker manager
creates missing paths with mkdir 0777 followed by chown 99 / chgrp 100
(dynamix.docker.manager/include/Helpers.php). So the first run works with no
intervention.
The --user override is about ownership, not about starting. Because those paths are
world-writable, the container also runs without it — but the files it writes end up owned
1000:1000 instead of 99:100.
Previously reported, now fixed upstream
Running as a UID other than 1000 used to make git reject the application directory
(fatal: detected dubious ownership in repository at '/opt/magic_mirror'), which broke the
updatenotification module. Fixed in
065f3b8b
by shipping /etc/gitconfig with a safe.directory entry. Verified: git rev-parse now
succeeds as UID 99 on all three variants.
The same round of upstream fixes
(64be145b)
made the application directory writable for arbitrary UIDs and widened the default
ipWhitelist to cover 10.0.0.0/8, 172.16.0.0/12 and IPv6 loopback — which is why this
template no longer documents a subnet workaround.
Installing modules
Third party modules go into /mnt/user/appdata/magicmirror/modules, then get enabled in
config.js. See the
module documentation and
the third party module list.
Modules with native dependencies may need a rebuild inside the container. Open a console
on the container and run npm install in the module's directory.
Image variants
The template's tag selector offers three server-only variants. All are amd64-capable, which is all Unraid needs.
| Tag | Base | Size | Shell | Notes |
|---|---|---|---|---|
wolfi-server |
Wolfi | 95 MB | sh |
Default. Minimal, regularly rebuilt, reduced CVE surface. |
debian-server |
Debian 13 | 108 MB | bash, sh |
Broadest compatibility for modules that compile native dependencies. |
alpine |
Alpine | 69 MB | sh |
Smallest. Some native modules may fail to build against musl. |
The template's Console Shell Command is set to sh, which all three provide. Switch it
to bash only if you select debian-server and want it.
The *-electron tags are intentionally not offered — they require a local display.
Repository contents
| File | Purpose |
|---|---|
templates/magicmirror.xml |
The Unraid container template |
ca_profile.xml |
Community Applications repository metadata |
CHANGELOG.md |
Versioned history of the template |
SBOM.md |
Software bill of materials and licence chain |
scripts/verify-repo.sh |
Quality gate: XML, encoding, git metadata |
scripts/check-links.sh |
Raw-URL reachability |
scripts/test-unraid-images.sh |
Reproduces the runtime verification on a host |
The quality gate and link check run in CI on every push.
Where to report problems
| Problem | Where |
|---|---|
| This Unraid template (paths, ports, permissions, CA listing) | Issues in this repo |
| The container image | khassel/magicmirror on GitLab |
| MagicMirror² itself or a module | MagicMirror² forum |
Screenshots
The CA listing currently ships without screenshots. MagicMirror²'s official promotional renders come from the MagicMirror-Website repository, which carries no license, so they are deliberately not bundled here.
To add screenshots, drop your own PNGs of a running instance into this repo and reference
them from templates/magicmirror.xml:
<Screenshot>https://raw.githubusercontent.com/heckpiet/magicmirror-unraid/main/preview.png</Screenshot>
Handover checklist
This repo is deliberately written so it can be transferred to, or forked by, another maintainer. If you take it over, update these values so they point at your copy:
-
templates/magicmirror.xml→<TemplateURL>,<ReadMe>,<Icon>,<Support> -
ca_profile.xml→<Icon>,<WebPage>, the support links in<Profile> -
README.md→ the repo URLs in the tables above -
LICENSE→ copyright holder - Re-run Validate and Scan at https://ca.unraid.net/submit/new, then submit
Credits
- MagicMirror² — MagicMirrorOrg, MIT
- Container image — Karsten Hassel, MIT
- Unraid template — this repo, MIT
The MagicMirror² name and logo belong to the MagicMirror² project.
apps.marketingCta.appInstallTitle
apps.marketingCta.appInstallDescription
apps.detail.sections.requirements
apps.detail.sections.categories
apps.downloadStats.title
apps.detail.sections.related
apps.detail.related.exploreCategories
apps.detail.related.exploreAllapps.detail.sections.links
apps.detail.sections.details
karsten13/magicmirror:wolfi-serverapps.detail.sections.runtime
- apps.detail.details.webui
http://[IP]:[PORT:8080]/- apps.detail.details.network
bridge- apps.detail.details.shell
sh- apps.detail.details.privileged
- false
- apps.detail.details.extraParams
--user 99:100
apps.detail.sections.configuration
Host port used to open the MagicMirror² interface. It must match the port set in config.js. Keep this port limited to your trusted network and do not forward it.
- apps.detail.config.target
- 8080
- apps.detail.config.default
- 8080
Persistent directory holding config.js and custom.css. A working config.js is created here on first start; edit it to enable modules and change the layout. Include this directory in backups.
- apps.detail.config.target
- /opt/magic_mirror/config
- apps.detail.config.default
- /mnt/user/appdata/magicmirror/config
Persistent directory holding third party modules. Include this directory in backups.
- apps.detail.config.target
- /opt/magic_mirror/modules
- apps.detail.config.default
- /mnt/user/appdata/magicmirror/modules
Timezone used for logs and displayed times, for example Europe/Berlin. If left empty the container falls back to UTC and logs a warning.
- apps.detail.config.target
- TZ
- apps.detail.config.default
- Europe/Berlin
Run mode of the container. Keep this at server on Unraid. The electron mode requires a locally attached display and will not work on a headless server.
- apps.detail.config.target
- MM_SCENARIO
- apps.detail.config.default
- server
Set to true to show the mouse cursor in the interface. Useful when MagicMirror² is displayed on a touchscreen or wall tablet.
- apps.detail.config.target
- MM_SHOW_CURSOR
- apps.detail.config.default
- false