Docker-first setup · Multi-user management · Media library · Metadata · Downloads · Emby-protocol clients · Cloud playback
中文 · Quick Start · Docker Compose · FAQ · Live Demo
MediaStationGo is a self-hosted media center for personal libraries, home NAS, and home-theater users.
It helps you:
The project is moving fast. With the default PostgreSQL deployment, back up both
data/andpostgres/.
postgres/, while runtime secrets and files live under data/.docker-compose.yml and start the service.adminadmin123The demo is for feature preview only. Do not save private API keys, tracker cookies, or personal data there.
Docker Compose is the recommended path. Beginners do not need .env, bare-metal binaries, or source builds. Use the single-image SQLite template if you want the smallest possible setup.
mkdir -p MediaStationGo
cd MediaStationGo
# Simplest option: one MediaStationGo container + SQLite
curl -fsSL https://raw.githubusercontent.com/ShukeBta/MediaStationGo/main/docker-compose.simple.yml -o docker-compose.yml
# Or tier 1: MediaStationGo + PostgreSQL
# curl -fsSL https://raw.githubusercontent.com/ShukeBta/MediaStationGo/main/docker-compose.yml -o docker-compose.yml
Edit docker-compose.yml:
vi docker-compose.yml
Start:
docker compose up -d
Open:
http://SERVER_IP:18080
Default login:
Username: admin
Password: admin123
The repository docker-compose.yml is the lightweight recommended template: no .env required, and by default it only starts MediaStationGo + PostgreSQL. This is the best starting point for most NAS users.
If you already have an older ./data/mediastation.db, the first start with the new compose file automatically imports it into PostgreSQL. Keep ./data; it still stores the JWT secret, runtime data, and the old SQLite migration source.
| Mode | Command | Best for |
|---|---|---|
| Single image: SQLite | docker compose -f docker-compose.simple.yml up -d |
Beginners and single-user setups that want one image only, no PostgreSQL/Redis |
| Lightweight: PG only | docker compose up -d |
Most NAS devices, lowest resource use |
| Standard: PG + Redis | docker compose -f docker-compose.standard.yml up -d |
Multi-user use and frequent Emby client refreshes |
| Search enhanced: PG + Redis + OpenSearch | docker compose -f docker-compose.search.yml up -d |
Huge libraries and future standalone search indexing |
Each compose file is standalone. Do not stack multiple -f files together.
The single-image docker-compose.simple.yml runs only MediaStationGo with a built-in SQLite database — the simplest starting point. Do not set MEDIASTATION_DATABASE_DSN there, or it switches back to PostgreSQL. Move up to the PostgreSQL modes for multi-user or high-concurrency use (keep ./data when you switch). Redis and OpenSearch are enhancement layers, not source databases. Do not enable OpenSearch by default on low-memory NAS devices.
The current Docker Compose setup uses PostgreSQL by default. SQLite is no longer the primary database in the recommended Docker deployment. The runtime database is controlled by:
environment:
MEDIASTATION_DATABASE_TYPE: postgres
MEDIASTATION_DATABASE_DSN: postgres://mediastation:mediastation@postgres:5432/mediastation?sslmode=disable
MEDIASTATION_DATABASE_DB_PATH is only used as a one-time migration source for old SQLite data:
docker compose up -d uses PostgreSQL and does not create a new SQLite primary database../data/mediastation.db exists, the first start with the new compose file imports it into PostgreSQL.settings table, so the old SQLite file is not imported again.Recommended SQLite to PostgreSQL upgrade flow:
docker compose pull mediastation-go
docker compose up -d --no-deps mediastation-go
docker compose logs -f mediastation-go
After you see sqlite data migrated to postgres, or after the web UI shows your users, libraries, and settings correctly, you can stop using the old SQLite file as a migration source.
To make the deployment PostgreSQL-only after migration, keep PostgreSQL selected and point the old SQLite migration path at a non-existent file:
Only do this after the web UI confirms that users, libraries, settings, and media rows are already present in PostgreSQL.
environment:
MEDIASTATION_DATABASE_TYPE: postgres
MEDIASTATION_DATABASE_DSN: postgres://mediastation:mediastation@postgres:5432/mediastation?sslmode=disable
MEDIASTATION_DATABASE_DB_PATH: /data/disabled-sqlite-migration.db
Then rename or move the old host-side SQLite file as an offline backup:
mv data/mediastation.db data/mediastation.sqlite.bak
For bare-metal or custom config.yaml deployments, use the same idea:
database:
type: postgres
dsn: postgres://mediastation:mediastation@127.0.0.1:5432/mediastation?sslmode=disable
db_path: ""
Do not delete ./postgres. After migration, it is the real primary database. Keep ./data too, because it stores the JWT secret and runtime files.
Both image sources are supported. Pick one and put it in image::
| Source | Image | Best for |
|---|---|---|
| GitHub Container Registry (GHCR) | ghcr.io/shukebta/mediastation-go:latest |
Recommended default, follows repository releases |
| Docker Hub | shukbet/mediastationgo:latest |
Backup source when GHCR is slow or unavailable |
To pin a version, first confirm the tag exists on the repository Packages page. Use this format:
image: ghcr.io/shukebta/mediastation-go:<version-tag>
# If GHCR does not have that tag, use Docker Hub as the backup:
# image: shukbet/mediastationgo:MediaStationGo-v0.0.72
For the simplest setup, keep GHCR latest.
Manual pull examples:
# GitHub Container Registry
docker pull ghcr.io/shukebta/mediastation-go:latest
# Docker Hub backup
docker pull shukbet/mediastationgo:latest
Focus on this part:
volumes:
- ./data:/data
- ./cache:/cache
- ./media:/media
- ./downloads:/downloads
Meaning:
| Host path | Container path | Purpose |
|---|---|---|
./data |
app /data |
Settings, JWT secret, old SQLite migration source; the primary DB is under ./postgres |
./cache |
app /cache |
Cache; safe to clean when needed |
./media |
/media |
Media libraries; use /media/... in the web UI |
./downloads |
/downloads |
Download directory and organization source |
./postgres |
PostgreSQL /var/lib/postgresql/data |
New default primary database; back this up |
./redis |
Redis /data |
Used only in standard mode; hot cache, rebuildable |
./opensearch |
OpenSearch /usr/share/opensearch/data |
Used only in search-enhanced mode; higher memory use |
If your NAS paths are:
/vol1/1000/Media
/vol1/1000/Downloads
change the compose file to:
volumes:
- ./data:/data
- ./cache:/cache
- /vol1/1000/Media:/media
- /vol1/1000/Downloads:/downloads
environment:
MEDIASTATION_MEDIA_DIR: /vol1/1000/Media
MEDIASTATION_DOWNLOAD_DIR: /vol1/1000/Downloads
Rules:
volumes is the real path on your host/NAS./media and /downloads unless you know why you are changing them./media/Movies or /media/TV../vol1/...; ./ means a folder under the current compose directory.D:/Media:/media and D:/Downloads:/downloads are fine.:ro; if you use organize/rename/ingest, the media mount must stay writable.The root docker-compose.yml follows this style:
```yaml services: mediastation-go: # Pick one image source: # GitHub Container Registry (GHCR): image: ghcr.io/shukebta/mediastation-go:latest # Docker Hub backup: # image: shukbet/mediastationgo:latest
restart: unless-stopped
init: true
depends_on:
postgres:
condition: service_healthy
# Browser: http://SERVER_IP:18080
ports:
- "18080:8080"
# Let the container reach qBittorrent running on the host:
# qB URL example: http://host.docker.internal:8085
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
# Application data. Back this up before upgrades.
- ./data:/data
- ./cache:/cache
# Beginners can create ./media and ./downloads.
# NAS users should replace source with real absolute paths.
# create_host_path=false prevents Docker from silently creating an empty
# folder when the host path is wrong.
- type: bind
source: ./media
target: /media
bind:
create_host_path: false
- type: bind
source: ./downloads
target: /downloads
bind:
create_host_path: false
environment:
TZ: Asia/Shanghai
PUID: "1000"
PGID: "1000"
MEDIASTATION_APP_HOST: 0.0.0.0
MEDIASTATION_APP_PORT: 8080
MEDIASTATION_APP_WEB_DIR: /app/web/dist
MEDIASTATION_APP_DATA_DIR: /data
# Lightweight mode uses PostgreSQL by default.
# Old SQLite data migrates from this path on first start.
MEDIASTATION_DATABASE_TYPE: postgres
MEDIASTATION_DATABASE_DSN: postgres://mediastation:mediastation@postgres:5432/mediastation?sslmode=disable
# After migration, change this to /data/disabled-sqlite-migration.db to disable the SQLite migration source.
MEDIASTATION_DATABASE_DB_PATH: /data/mediastation.db
MEDIASTATION_CACHE_CACHE_DIR: /cache
# Use /media and /downloads in the web UI and downloader by default.
# Only set MEDIASTATION_*_DIR to real host paths when migrating old
# libraries/tasks that already stored host paths.
MEDIASTATION_MEDIA_DIR: /media
MEDIASTATION_MEDIA_CONTAINER_DIR: /media
MEDIASTATION_DOWNLOAD_DIR: /downloads
MEDIASTATION_DOWNLOAD_CONTAINER_DIR: /downloads
postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: mediastation POSTGRES_USER: mediastation POSTGRES_PASSWORD: mediastation volumes: - ./postgres:/var/lib/postgresql/data
$ claude mcp add MediaStationGo \
-- python -m otcore.mcp_server <graph>