A self-hosted video library that stays out of your way.
⚠️ Security noticeThis application was built with AI assistance and is designed for local or trusted-network use. Exposing it to the public internet without an additional access-control layer (reverse proxy with auth, VPN, etc.) is done at your own risk.
You've got a folder full of videos. Family recordings, trip footage, tutorials you've saved, a little knowledge base you've been building. They're already organised the way you want them. But browsing them means opening a file manager, squinting at filenames, and digging through nested folders to find that one video.
You've probably looked at Plex or Jellyfin. They're great pieces of software — for a different use case. They're built for media libraries that need metadata scraped from the internet, posters fetched, agents configured, databases maintained. That's a lot of infrastructure for a collection of MP4s you already know how to organise. And the moment you try to share something, you're back to sending a 2 GB file attachment that crashes in someone's inbox.
Incastr is the other thing. No metadata fetching. No transcoding service to keep running. No agents. You point it at a folder, it scans the files, and they appear in a clean browsable interface — with thumbnails, tags, and search. That's it.
Your folder structure is your organisation.
/media/videos/
├── Family/
│ ├── summer-2023.mp4
│ └── christmas.mkv
├── Tutorials/
│ ├── docker-basics.mp4
│ └── networking/
│ └── dns-explained.mp4
└── WatchLater/
└── interesting-talk.mp4
Mount that folder, point Incastr at it, run a scan. You get three categories: Family, Tutorials, WatchLater. No configuration, no metadata to fill in. Incastr always uses the first-level subfolder as the category name — deeper nesting is fine, a video at Tutorials/networking/dns-explained.mp4 still lands in Tutorials.
Need to move a video to a different category? Rename a file? You can do all of that from the interface, and it moves the actual file on disk.
Sending a raw video file is terrible. It's too large for email, awkward in messaging apps, and requires the other person to download the whole thing before watching anything.
With Incastr you get two kinds of shareable links — no account needed on the recipient's end:
Single video — Set any video to Unlisted and copy the link. Anyone with the URL can watch it directly in their browser.
Whole category — In your library, click the share icon next to any category. Incastr generates a private link to the entire category. You can disable it temporarily, set an expiry date, or revoke it entirely. The people you share it with see a clean grid of all the videos in that category and can watch them without logging in.
- Thumbnails — generated automatically by ffmpeg during scan, no manual work
- Categories — your folder names, zero setup required
- Tags — add fine-grained labels to individual videos
- Full-text search — across title, description, category, and tags
- File management — rename files, move them to a different category, delete from disk — all from the browser
- Multi-user — each user has their own library, folders, and shares
- Public landing page — optionally make some videos visible to anyone who visits your instance
- Pagination — 16 videos per page
1. Get the compose file
git clone https://github.com/tiritibambix/incastr.git
cd incastr2. Edit docker-compose.yml
volumes:
- /path/to/your/videos:/media/videos # remove :ro if you want to delete/move files from the browser
environment:
- SECRET_KEY=change-this-to-something-long-and-random
- MEDIA_DIR=/media/videos # auto-registers this folder for your admin account on first boot3. Start
docker compose up -dOpen http://your-server:8420, create your account — the first user is automatically an admin — then hit Scan all in Settings. Your videos appear within seconds.
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
required | JWT signing key — use a long random string in production |
MEDIA_DIR |
— | Path to auto-register as a video folder for admin accounts on startup |
THUMBS_DIR |
/data/thumbs |
Where thumbnails are stored |
SCAN_INTERVAL_MINUTES |
60 |
How often to auto-scan for new files (0 = disabled) |
ALLOW_REGISTRATION |
true |
Set to false to lock down new sign-ups |
ACCESS_TOKEN_EXPIRE_MINUTES |
60 |
Login session length |
MAX_SCAN_DEPTH |
10 |
How deep to recurse into subfolders |
FIRST_ADMIN_USERNAME |
— | Create an admin account on first startup (headless / CI setup) |
FIRST_ADMIN_PASSWORD |
— | (used alongside the above) |
FIRST_ADMIN_EMAIL |
— | (used alongside the above) |
Incastr serves files directly — no transcoding, no re-encoding. Supported container formats:
.mp4 .mkv .avi .mov .webm .m4v .ts .flv .wmv .mpg .mpeg .m2ts .mts
H.264/MP4 plays in every browser without any issues. MKV and HEVC work if your browser's codec support covers them. If a file doesn't play, re-encoding to H.264/AAC in an MP4 container is the reliable fix.
Incastr runs on port 8420 by default. Behind Nginx Proxy Manager or any other reverse proxy, just point it at that port and enable HTTPS — no special configuration on Incastr's side.
It runs fine on modest hardware. A Raspberry Pi or a cheap VPS handles a personal collection without breaking a sweat. SQLite keeps the footprint small — no external database to manage.
# Backend (Python 3.12+)
pip install -r requirements.txt
uvicorn backend.main:app --reload
# Frontend
cd frontend
npm install
npm run dev # proxies /api to localhost:8000- Backend — Python 3.12, FastAPI, SQLAlchemy (async), SQLite, Alembic, ffmpeg
- Frontend — React 18, TypeScript, Vite, Tailwind CSS
- Container — single Docker image, multi-stage build
MIT