Skip to content

motionEye

Lightweight web frontend for the venerable motion daemon. Motion-triggered recording, timestamp overlays, email notifications — no AI object detection. Pi-friendly (runs comfortably on a Pi 3/4/5 or any Linux SBC).

This is the simple / nostalgic camera bundle. If you want AI ("was there a person at the front door?") or MCP-callable camera tools from your AI assistant, install Frigate instead.

What You Get

  • Web UI for configuring RTSP/ONVIF and USB (V4L2) cameras
  • Motion-triggered recording with configurable per-camera retention
  • Email notifications on motion
  • Default timezone/timestamp overlay
  • Crow's Nest iframe tab — single-pane access alongside your other tools

What You Don't Get

  • AI object detection (Frigate does this)
  • MCP tools (list_cameras, list_events, snapshot, etc.) — motionEye's API uses session-cookie + CSRF admin auth that isn't worth wrapping for v1
  • Real-time event notifications into Crow's notification center

When to Use vs Frigate

motionEyeFrigate
USB webcams (V4L2)YesNo
RTSP / ONVIFYesYes
AI object detectionNoYes
MCP toolsNo (iframe only)Yes (7 tools)
HardwarePi-class friendlyx86 preferred (Pi 5 + Coral works)
UIClassic web adminModern React SPA
Footprint256 MB RAM baseline2 GB RAM baseline

You can install both. They coexist under the same "Cameras" category in the Crow's Nest.

Setup

Install the bundle

"Crow, install the motionEye bundle"

Or via Extensions in the Crow's Nest → find motionEye under Cameras.

Uses the official ghcr.io/motioneye-project/motioneye:latest multi-arch image (amd64, arm64, armhf, riscv64), version 0.43.1 as of this writing.

Exposes port :8765 on loopback. The Nest iframe loads motionEye from that loopback address, so there's no public exposure of the camera UI.

First login

Default credentials on first start:

  • Username: admin
  • Password: (empty)

Rotate immediately via Settings → General → Admin Password. If the bundle is exposed to any untrusted network (which it shouldn't be by default — Crow binds it to loopback), an empty admin password is a catastrophic hole.

Add a camera

Inside the Web UI (either the Nest iframe tab or http://localhost:8765 on the host):

  1. Click the hamburger menu → add camera
  2. Pick the camera type:
    • Network camera — paste an RTSP URL (e.g. rtsp://user:pass@192.168.1.42:554/stream)
    • V4L2 camera — pick a /dev/videoN device (USB webcam). You'll need to pass the device through in docker-compose.yml: add devices: - "/dev/video0:/dev/video0" under the service, then restart.
  3. Click OK
  4. Configure motion detection sensitivity under Settings → Motion Detection
  5. Configure retention under Settings → Movies → preserve movies (day count; default is unlimited — set this or your disk fills up)

Storage

Recordings land in ~/.crow/data/motioneye/media/ on the host. post-install.sh warns if free disk drops under 50 GB and aborts below 10 GB.

Per-camera retention is in the camera's Settings → Movies. Set it. motionEye defaults to unlimited.

Troubleshooting

Empty iframe / "connection refused" The container isn't running. Start it:

bash
cd ~/.crow/bundles/motioneye && docker compose up -d

USB webcam not detected motionEye needs the device passed through explicitly:

yaml
# ~/.crow/bundles/motioneye/docker-compose.yml
services:
  motioneye:
    # ...
    devices:
      - "/dev/video0:/dev/video0"
    # match the device index to what `ls /dev/video*` shows on the host

Then crow bundle restart motioneye.

Recordings filling the disk You didn't set per-camera retention. Open each camera's Settings → Movies → preserve movies → pick a day count (7, 14, 30).

Tile does not appear in Extensions panel The bundle installed to ~/.crow/bundles/motioneye/ but the panel symlink is missing. Re-run:

bash
~/crow/scripts/crow bundle install motioneye

Then restart the gateway.

Need LAN access to motionEye directly (e.g., for the phone app) By default :8765 is loopback-only. To expose on LAN, edit ~/.crow/bundles/motioneye/docker-compose.yml:

yaml
ports:
  - "8765:8765"      # all interfaces (not just loopback)
# or to a specific tailnet IP:
#  - "100.x.x.x:8765:8765"

Then crow bundle restart motioneye. Know that motionEye's built-in auth is weaker than the Crow gateway; prefer keeping it loopback and using the Nest iframe.

Security Notes

  • Default admin password is empty — rotate on first login, always.
  • Loopback-only port binding in the default compose file.
  • Never map /dashboard/motioneye into a Tailscale Funnel prefix — the rejectFunneled middleware enforces this, and tests/auth-network.test.js has a regression test for it.
  • motionEye's config dir contains the admin password hash; backup ~/.crow/data/motioneye/config/ if you change passwords.

Future Work

  • MCP tool wrapper — motionEye has admin endpoints for snapshot/start/stop/config-get behind session cookies + CSRF; a future v2 could wrap them so the AI can ask "list my motionEye cameras." Not prioritized while Frigate covers the MCP path.
  • USB autodetect — the current bundle requires manual devices: edit for USB cams. A future post-install could scan /dev/video* and generate the compose entries.

Released under the MIT License.