Self-hosted media tracking for people who miss Trakt and TV Time.
Movies, TV, anime, manga, books, comics, games, board games, music, and podcasts.
One library, one history, and one set of stats for everything you watch, read, play, or listen to.
Demo · Docker Image · Wiki · Releases
Floppy is a self-hosted, all-in-one media tracker and personal media diary, a broader alternative to Trakt, Letterboxd, and TV Time for people who want one place for everything they watch, read, play, or listen to. It gives you a real progress view that tells you what to watch next, a unified history you can actually scan, recap-style statistics, shareable lists, owned-media collections, and integrations that sync instead of asking you to upload a file every few months.
It runs in Docker, keeps your data on your own hardware, and treats music and podcasts as first-class media rather than bolt-ons.
Try it first: the demo instance is open with demo / demodemo.
Floppy was formerly published as the dannyvfilms/Yamtrack fork. Old links redirect here.
One stack, app plus Redis. Save it as docker-compose.yml and run docker compose up -d, or paste it straight into a Portainer stack.
services:
floppy:
image: ghcr.io/dannyvfilms/floppy:latest
container_name: floppy
restart: unless-stopped
depends_on:
redis:
condition: service_healthy
environment:
- SECRET=change_me_to_a_long_random_string
- REDIS_URL=redis://redis:6379
- REGISTRATION=True
- DEMO_ACCOUNT_ENABLED=False
- TZ=America/Chicago
- TMDB_API=your_tmdb_api_key
volumes:
- floppy_db:/floppy/db
ports:
- "8000:8000"
redis:
image: redis:8-alpine
container_name: floppy-redis
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes", "--save", "", "--maxmemory", "256mb", "--maxmemory-policy", "volatile-lru"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 10
volumes:
- redis_data:/data
volumes:
floppy_db:
redis_data:Open http://localhost:8000, create your account, then set REGISTRATION=False and redeploy so strangers can't sign up.
DEMO_ACCOUNT_ENABLED=False is set above on purpose: it otherwise defaults to True and provisions a publicly known demo / demodemo login after migrations. Leave it off unless you actually want a shared demo account.
That's the whole install. SECRET is the only variable you truly must set; TMDB_API is what makes movie and TV metadata work, and every other API key is optional until you want that media type. Everything else — Postgres, reverse proxies, the full environment variable list, Docker Run, Portainer specifics — is in Configuration and deployment further down.
Floppy combines the jobs people often split between a watchlist, a media diary, and an owned-media collection: Time Left progress, a unified history feed, recap-style stats, public list sharing, and all-in-one tracking across every media type you care about.
- Music: artist and album pages, track-level history and scoring, play-count and listening-time statistics, bulk save and mark-all-listened; MusicBrainz-backed metadata with discography sync and cover art; fully native in history, search, home rows, and collection, not a thin importer.
- Podcasts: dedicated show and episode pages, episode-level tracking, mark-all-played; Pocket Casts account sync as a live integration, not a one-shot file import; podcast listening appears naturally in history, runtime stats, and search.
- Collections / owned media: track what you physically or digitally own with copy-level detail: source, resolution, HDR, format, codec, and bitrate; filtered collection views, per-item collection status tied into detail pages and list/smart-list rules; supports Plex collection sync.
- Discover: personalized recommendation rows that improve with use: genre, studio, cast, and tag affinity built from your library; not-interested and hide feedback that sticks; background refresh so rows stay current, individually refreshable from the UI, not a static recommendations page.
- History and statistics: history is a filterable feed with month navigation, media-type and genre filters, inline duplicate-play cleanup, and a delete flow; statistics offer explicit refresh, compare mode, custom date ranges, top-talent breakdowns, and per-type splits covering TV, film, music, podcasts, and reading with pages read, top authors, reading streaks, and listening time.
- Lists: public, social, and smart: public and private lists, custom slugs, public profile pages; RSS and JSON feeds per list; smart-list rules for collection status, release state, platform, origin, author, and tags; recommendations with approval flow; list completion percentages and media-type breakdowns in the index; Trakt list and watchlist import; sort by rating, progress, release date, last watched, or custom manual order.
- Integration coverage: Plex full library import, watchlist sync, and ratings sync; Pocket Casts account sync; Last.fm history import and live poll; Audiobookshelf account import; Radarr and Sonarr scheduled library sync; Seerr webhook auto-add, each with dedicated settings and status display.
- Richer metadata and title control: localized and original titles switchable per user preference; critic ratings and popularity scores displayed; game-length data; manual metadata overrides; metadata-provider preference; image refresh flows.
- People, studios, and credit browsing: actor, director, author, and studio pages with filmographies and top works; person credits visible from detail pages rather than hidden as tooltip data; author pages with top-read breakdowns.
- Careful anime handling: proper separation of anime and TV library concerns so mixed libraries stay organized; anime-specific season and episode navigation; grouped-anime routing for franchise-spanning series.
- Richer episode and book workflows: episode detail pages with individual scoring; bulk episode save; drop an episode without logging it to history; book-specific: barcode and ISBN scanning from a photo, percentage-based reading progress, top-authors stats, and more resilient import flows.
- Configurable home screen: choose what rows appear and in what order; rows from library queries, custom lists, smart lists, or recently played but not rated; direction and media-type filters stored per user.
- Configurable table columns: choose and reorder visible columns per view, with media tables and list-detail tables configured independently; available columns include critic rating, episodes left, time left, time to beat, runtime, time watched, last watched, next air date, date added, popularity, and more.
- Scheduled backups and export management: recurring export scheduling with media-type and list inclusion options; export history and backup destination visible in settings.
- Account security: TOTP authenticator setup and management; recovery codes; password recovery via authenticator or recovery code; session duration as a per-user preference.
- Deep preferences: sort modes for critic rating, popularity, runtime, time to beat, plays, time watched, release date, last watched, next air date, and time left; display preferences for duration format, rating scale, stats default range, compare mode, mobile grid density, subtitle visibility on cards, localized vs. original title display, progress-bar visibility, planned-item home visibility, and obfuscating unseen episode titles.
- A livelier UI: a now-playing card showing what is actively playing via Plex, Jellyfin, or Emby webhook; explicit stale and refreshing indicators on history and stats with one-click refresh; lazy-loaded covers and asynchronous fragments throughout.
- Better search and add flows: music-native search that creates artist and album entries from search results; improved anime and localized-title search results.
- Deeper filters: rated and unrated, collected and not collected, caught-up and not-caught-up, no-status, language, country, platform, origin, format, author, tag inclusion, and tag exclusion; smart-list rules use the same expanded vocabulary, making them meaningfully programmable.
- More reliable under load: WAL mode and timeout configuration for SQLite; retry logic for lock and I/O failures; prioritized background task queues for a smoother experience with large libraries.
- Integration settings and import UX: import history and status visible per integration in settings; watchlist-only and collection-update-only import modes; Seerr allowed usernames and defaults persisted as preferences; per-user Plex webhook library selection.
Multi-user accounts with OIDC and social login; calendar and iCalendar feeds for upcoming releases; release notifications through Apprise; Jellyfin, Plex, and Emby playback integrations; imports from Trakt, Simkl, MyAnimeList, AniList, Kitsu, Steam, Goodreads, StoryGraph, Hardcover, IMDb, HowLongToBeat, Grouvee and more; a REST API at /api/v1 with an MCP server; and CSV export/import so your data is always yours to take elsewhere.
Change and configure what content is most important to you, making things faster and more tailored to your needs.
Statistics are designed for recap-style browsing across time ranges and media types.
History keeps watches and listens in one place so recent activity is easy to scan.
Lists can be shared publicly, surfaced on profiles, and used as more than a private backlog.
Collections add ownership context alongside tracking, with room for copy-level detail.
|
|
Floppy started as a fork of Yamtrack and has diverged substantially since — the rename exists so the two projects stop being confused for each other. The upgrade path is intentionally boring:
- Your data moves over as-is. Export a CSV from Yamtrack and import it under Settings → Import; the formats are identical. Floppy's own backups export as
floppy_<date>.csvand use the same format, so nothing is one-way. - Your existing container keeps working. If you already run this project's image, the rename doesn't break your compose file: the old
/yamtrack/dbmount path still resolves inside the image, and pre-renameYAMTRACK_*environment variables are still read. - One thing to update: the image moved to
ghcr.io/dannyvfilms/floppy. Point your compose file at the new path when convenient — the old path stops receiving new builds.
Floppy retains Yamtrack's core tracking, import, and self-hosting workflows, with the additional capabilities described above.
Floppy exposes a REST API at /api/v1 and ships an MCP server. Integrations meant for Floppy should target this repository and the ghcr.io/dannyvfilms/floppy image — upstream Yamtrack does not carry Floppy's API surface, media types, or integration workflows, so "compatible with Yamtrack" and "compatible with Floppy" are not interchangeable claims.
- Interactive docs:
/api/docs/on any instance · raw schema:/api/schema/ - Auth:
Authorization: Bearer <token>orX-API-Key: <token>, from Settings → Advanced - Full reference: API and MCP Server
Floppy uses PostgreSQL only when DB_HOST is set. Without it, it uses SQLite at /floppy/db/db.sqlite3. DATABASE_URL is not supported — set the individual DB_* variables.
services:
floppy:
image: ghcr.io/dannyvfilms/floppy:latest
container_name: floppy
restart: unless-stopped
depends_on:
- db
- redis
environment:
- SECRET=your-secret-key-here-change-this
- REDIS_URL=redis://redis:6379
- DEMO_ACCOUNT_ENABLED=False
- TZ=America/New_York
- DB_HOST=db
- DB_NAME=floppy
- DB_USER=floppy
- DB_PASSWORD=change-this-password
- DB_PORT=5432
ports:
- "8000:8000"
db:
image: postgres:16-alpine
container_name: floppy-db
restart: unless-stopped
environment:
- POSTGRES_DB=floppy
- POSTGRES_USER=floppy
- POSTGRES_PASSWORD=change-this-password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:8-alpine
container_name: floppy-redis
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes", "--save", "", "--maxmemory", "256mb", "--maxmemory-policy", "volatile-lru"]
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:Already running Postgres with
DB_NAME=yamtrack? Leave those values alone. Renaming the database, role, or password against an existing volume breaks the deployment.
No compose file needed:
docker network create floppy-net
docker run -d \
--name floppy-redis \
--network floppy-net \
--restart unless-stopped \
-v floppy-redis-data:/data \
redis:8-alpine \
redis-server --appendonly yes --save "" --maxmemory 256mb --maxmemory-policy volatile-lru
docker run -d \
--name floppy \
--network floppy-net \
--restart unless-stopped \
-e ALLOWED_HOSTS=floppy.yourdomain.com,your.lan.ip.address \
-e DEBUG=False \
-e DEMO_ACCOUNT_ENABLED=False \
-e IGDB_ID=your_igdb_client_id \
-e IGDB_SECRET=your_igdb_client_secret \
-e LASTFM_API_KEY=your_lastfm_api_key \
-e MAL_API=your_mal_client_id \
-e PGID=1000 \
-e PUID=1000 \
-e REDIS_URL=redis://floppy-redis:6379 \
-e REGISTRATION=True \
-e SECRET=your_django_secret_key \
-e TMDB_API=your_tmdb_api_key \
-e TVDB_API_KEY=your_tvdb_api_key \
-e TZ=America/Chicago \
-v floppy-db:/floppy/db \
-p 8000:8000 \
ghcr.io/dannyvfilms/floppy:latestLeave REGISTRATION=True for your first run, then recreate the container with False once your account exists.
Prefer Stacks over Containers → Add container. Stacks let you paste a complete Compose configuration and avoid missing required volumes or environment variables. This is also the recommended path for Unraid: rather than installing from a Community Applications template and standing up Redis separately, paste one compose file into a stack via the Portainer plugin and both containers come up together.
- Go to Stacks → Add Stack
- Name it
floppy - Paste one of the compose configurations above
- Set
SECRETto a secure random string, and fill in whichever metadata API keys you have - Deploy the stack
- Create your account while
REGISTRATION=True, then set it toFalseand redeploy
If you use Containers → Add container anyway: always set SECRET and REDIS_URL; for SQLite mount persistent storage to /floppy/db; for PostgreSQL set the DB_* variables on the Floppy container and persist /var/lib/postgresql/data on the Postgres container; publish port 8000; and leave Command and Entrypoint empty.
The only universally required variable is SECRET. For Docker installs you should also set REDIS_URL.
Optional but recommended:
TMDB_API- movie and TV metadata from TMDBTVDB_API_KEY/TVDB_PIN- TVDB-backed metadata and grouped anime support (TVDB_PINis your Subscriber PIN, only required for user-supported API keys)MAL_API- MyAnimeList Client ID for anime metadata (register here)IGDB_ID/IGDB_SECRET- game metadata from IGDBSTEAM_API_KEY- Steam game importsBGG_API_TOKEN- board game metadata from BoardGameGeekHARDCOVER_API- Hardcover book metadata/importsCOMICVINE_API- comic metadataLASTFM_API_KEY- Last.fm integration and scrobble pollingTRAKT_API/TRAKT_API_SECRET- Trakt private-profile OAuth importsURLS- your public URL if using a reverse proxy, for examplehttps://floppy.mydomain.comADMIN_ENABLED- set toTrueto enable the Django admin interface at/admin/(see the Admin Guide)WEB_CONCURRENCY/GUNICORN_THREADS- optional web server concurrency overrides. Leave both unset to use the detected host profile, especially on small or swapless hosts. Total concurrent requests = workers x threadsFLOPPY_RESOURCE_TIER-standard,constrained, orminimal. Floppy normally detects this from the host's memory, swap and CPU and scales its process count, batch sizes and background task cadence to match, so you should not need to set it. Use it to force a tier if detection guesses wrong - for examplestandardon a host whose cgroup understates the memory actually availableFLOPPY_REDIS_MAXMEMORY- Redis memory ceiling Floppy applies at startup when Redis has none of its own, as bytes or a size like256mb. Set it to0to leave your Redis configuration completely untouched. Floppy never overrides amaxmemoryyou set yourselfDEBUG- leave unset orFalsein production; enabling it slows every request (debug toolbar, no template caching) and is only meant for troubleshootingREGISTRATION- set toTrueto allow new signups (needed for your first account), then set toFalseafterwardDEMO_ACCOUNT_ENABLED- defaults toTrue, provisioning the built-indemo/demodemoaccount after migrations. The examples above set it toFalse; only turn it on if you want a shared demo loginALLOWED_HOSTS/PUID/PGID-ALLOWED_HOSTSis a comma-separated list of hostnames/IPs Django will accept requests for;PUID/PGIDset the file-ownership user/group inside the container (match your host user, e.g. Unraid's99/100, if you hit permission errors)
For the complete list, see the Environment Variables documentation.
Example .env file:
TMDB_API=API_KEY
TVDB_API_KEY=TVDB_API_KEY
TVDB_PIN=SUBSCRIBER_PIN (Optional, only for user-supported keys)
MAL_API=CLIENT_ID
IGDB_ID=IGDB_ID
IGDB_SECRET=IGDB_SECRET
STEAM_API_KEY=STEAM_API_SECRET
BGG_API_TOKEN=BGG_API_TOKEN
HARDCOVER_API=HARDCOVER_API
COMICVINE_API=COMICVINE_API
LASTFM_API_KEY=LASTFM_API_KEY
SECRET=SECRET
DEBUG=FalseFloppy sizes itself to the machine it finds. On startup it reads the container's cgroup
memory and CPU limits plus /proc/meminfo, picks a resource tier, and scales its process
count, batch sizes, and background task cadence accordingly. The chosen tier is logged on
the first line of the container's output:
[entrypoint] resources tier=minimal mem=1.9GiB swap=0 cpus=2 -> gunicorn 1x2, celery queues "celery,interactive,discover"
- standard (3 GB+): one threaded gunicorn worker and two Celery workers. Discover and beat run with the background worker; the interactive worker stays separate so webhook scrobbles are never stuck behind a backfill.
- constrained (under 3 GB): the same lean process layout as standard, with smaller worker-recycling and cache budgets.
- minimal (under 1.5 GB): one gunicorn worker and a single Celery worker serving every queue.
On a small host, swap matters as much as the memory figure. Each Celery worker holds
its own full copy of the application, so below about 6 GB a host with no swap gets bumped
one tier stricter — a memory spike with nowhere to page is what turns a slow container
into a hung one. If you have disabled swap to spare an SSD, 2 GB of RAM lands on
minimal, which is supported. Above 6 GB, missing swap changes nothing.
Two things worth knowing:
- If you set no
mem_limiton the Floppy container, it sees the whole host's memory. That is usually what you want on a dedicated VM. On a shared host, setmem_limitso Floppy sizes itself to its share rather than to the machine. - Floppy gives Redis a memory ceiling and an LRU eviction policy at startup if Redis has
none of its own, so an unbounded cache can't exhaust the host. It never overrides a
maxmemoryyou configured yourself. Rundocker exec floppy python manage.py tune_redis --dry-runto see what it would do.
Override any of it with FLOPPY_RESOURCE_TIER, WEB_CONCURRENCY, GUNICORN_THREADS, or
FLOPPY_REDIS_MAXMEMORY.
Use scripts/benchmark_memory.sh to compare two already-built Floppy images without
touching an existing deployment. It starts each image in its own disposable Compose project
with SQLite, samples the healthy warmed container three times, and writes CSV/JSON reports
under a temporary directory:
scripts/benchmark_memory.sh \
--baseline-image floppy:baseline \
--candidate-image floppy:memory-testThe report distinguishes cgroup usage from summed process PSS/RSS and Redis usage, so shared preloaded pages and the separate Redis container are visible instead of being counted as a single opaque number. Build the candidate image locally before running the comparison.
If an existing deployment has WEB_CONCURRENCY or GUNICORN_THREADS set from an
older fixed-size configuration, remove those overrides before upgrading on a
small host so the automatic resource profile can take effect.
Source-based deployments must load Floppy's Gunicorn configuration so the host-derived worker settings and database/Redis fork-safety hooks are active:
cd /path/to/floppy/src
gunicorn --config python:config.gunicorn config.wsgi:applicationDo not start the service with bare gunicorn config.wsgi:application; that
skips the shipped configuration and its process lifecycle hooks.
The migration conflict involving 0147_item_calendar_checked_at and
0148_merge_duplicate_item_buckets is fixed in current images by the
0149_merge_20260806_1556 merge migration. Pull the updated image before
restarting instead of generating a local merge migration:
docker compose pull floppy
docker compose up -d --force-recreate floppyBecause latest is mutable, pin a versioned image tag when reproducible
upgrades matter.
- SQLite stores the app database at
/floppy/db/db.sqlite3; persist/floppy/db. (Pre-rename/yamtrack/dbmounts still resolve, so existing setups keep working.) - Do not put
/floppy/dbon a network filesystem (NFS, SMB/CIFS, or a NAS "share" mounted into Docker). Floppy's SQLite database uses WAL mode, which SQLite's own documentation warns is unsafe over network filesystems because they don't reliably support the locking it depends on - this can corrupt the database under normal concurrent use. Use local/block storage for/floppy/db, or setDB_HOSTto use PostgreSQL if only network storage is available. - PostgreSQL stores its database files at
/var/lib/postgresql/data; persist that path on the Postgres container. - Redis stores sessions and background-task state; resetting Redis can log users out, but it should not delete accounts if the database is persisted.
- Do not assume
DATABASE_URLenables PostgreSQL. Floppy uses Postgres only whenDB_HOSTis set.
If you import from a private Trakt profile, configure OAuth first:
- Create an app in Trakt API Apps.
- Add this Redirect URI in the Trakt app:
https://your_domain.com/import/trakt/private
- Set these environment variables:
TRAKT_API= your Trakt client IDTRAKT_API_SECRET= your Trakt client secret
Behind a reverse proxy, also set URLS=https://your_domain.com so Floppy generates the correct external callback URL.
If you are behind a reverse proxy (Nginx, Traefik, Caddy, and so on) and see a 403 Forbidden, add your URL to the environment:
environment:
- URLS=https://floppy.mydomain.comMultiple origins can be comma-separated, for example https://floppy.mydomain.com,https://floppy-alt.mydomain.com.
If callback URLs for AniList and other imports come out wrong, add:
environment:
- USE_X_FORWARDED=TrueNote: With a Cloudflare Tunnel or any HTTPS-terminating proxy, also set
USE_X_FORWARDED_PROTO=True— otherwise Django cannot detect the correct scheme and CSRF checks will fail.
- If you intended to use PostgreSQL, confirm
DB_HOSTis set.DATABASE_URLalone will not enable Postgres. - If you intended to use SQLite, confirm
/floppy/db(or the legacy/yamtrack/db) is mounted to persistent storage. - If you were only logged out but can sign in again, Redis/session data was reset; your account database is still intact.
- Do not remove database volumes during updates unless you intentionally want a fresh install.
The image lives at ghcr.io/dannyvfilms/floppy:
:latest- the latest commit on thelatestbranch:release- the latest commit on thereleasebranch, or the stable alias for a GitHub release tag:vX.Y.Z- versioned release builds from GitHub release tags
For contributing or customizing locally:
git clone https://github.com/dannyvfilms/Floppy.git
cd Floppy
docker run -d --name redis -p 6379:6379 --restart unless-stopped redis:8-alpine
python -m pip install -U -r requirements-dev.txtCreate a .env with at least SECRET, DEBUG=True, and whichever API keys you need (same names as the Docker list above), then:
cd src
python manage.py migrate
python manage.py createsuperuser
python manage.py runserverCelery and Tailwind run in separate terminals:
celery -A config worker --queues interactive --hostname celery-interactive@%h --loglevel DEBUG
celery -A config worker --queues celery --beat --scheduler django --hostname celery@%h --loglevel DEBUGnpx @tailwindcss/cli -i ./static/css/input.css -o ./static/css/main.css --watchVisit http://localhost:8000. A demo / demodemo account is provisioned after migrations; set DEMO_ACCOUNT_ENABLED=False to disable it. See CONTRIBUTING.md before opening a pull request.
- Star the repository if you want to help more people find Floppy.
- Open an issue for bugs, or for feature requests and ideas.
- Open a pull request if you want to contribute code, docs, or polish.
AGPL-3.0.
Floppy began as a fork of FuzzyGrim/Yamtrack and still shares its foundation and data model — Yamtrack CSV exports import directly, and this repository will keep showing the fork link. Since then it has grown into a distinct project with its own direction: a Trakt-replacement daily driver for people who want something more opinionated and more feature-dense. Thanks to FuzzyGrim and Yamtrack's contributors for the groundwork.


