@@ -6,17 +6,182 @@ catalog data and separate viewing state for each family user.
66
77## Install
88
9- The full walkthrough — from an empty machine to a household watching,
10- downloading to phones, and pairing TVs — is in ** [ docs/INSTALL.md] ( docs/INSTALL.md ) ** .
9+ Two files in a folder, no clone. The server image is pulled from
10+ ` ghcr.io/doselibrary/dose ` . The full walkthrough, from an empty machine to a
11+ household watching, downloading to phones, and pairing TVs, is in
12+ ** [ docs/INSTALL.md] ( docs/INSTALL.md ) ** .
1113
12- The short version:
14+ You need Docker with Compose 2.23.1 or newer, your movies and shows in two
15+ folders, and a free [ TMDB API read-access token] ( https://www.themoviedb.org/settings/api )
16+ for artwork and metadata.
1317
14- 1 . Copy ` .env.example ` to ` .env ` ; set ` POSTGRES_PASSWORD ` , your media paths,
15- and a TMDB token.
16- 2 . Point ` dose.local ` at this machine (router DNS entry, or a hosts line).
17- 3 . ` docker compose pull && docker compose up -d ` and open ` https://dose.local ` .
18- The server image comes from ` ghcr.io/doselibrary/dose ` , built by GitHub
19- Actions from the ` v2 ` branch; ` compose.build.yaml ` builds from source instead.
18+ ** 1. ` .env ` ** — fill in the password, your media folders, and the token.
19+ Windows paths use forward slashes.
20+
21+ ``` sh
22+ # Movies and shows, mounted read-only.
23+ DOSE_MOVIES_PATH=D:/Media/Movies
24+ DOSE_SHOWS_PATH=D:/Media/Shows
25+ # Letters, numbers, underscores, and hyphens only.
26+ POSTGRES_PASSWORD=replace-with-a-long-unique-password
27+ # Free TMDB API read-access token (the long v4 one).
28+ TMDB_API_TOKEN=
29+ # Image tag to run: latest, a release like 1.2.3, or sha-<commit>.
30+ DOSE_VERSION=latest
31+ ```
32+
33+ ** 2. ` compose.yaml ` ** — copy as-is. This is the same file the repository ships.
34+
35+ ``` yaml
36+ # Dose — self-hosted household media server.
37+ #
38+ # Self-contained: this file plus a `.env` next to it is a complete install.
39+ # Nothing else from the repository is needed; the server image is pulled from
40+ # GitHub Container Registry and the proxy config is inlined below.
41+ #
42+ # docker compose pull && docker compose up -d
43+ #
44+ # Needs Docker Compose 2.23.1 or newer (inline `configs: content:`).
45+
46+ services :
47+ dose :
48+ # Published by .github/workflows/docker.yml on every push to v2. Pin a
49+ # release with DOSE_VERSION=1.2.3 in .env; build from source instead with
50+ # compose.build.yaml.
51+ image : ghcr.io/doselibrary/dose:${DOSE_VERSION:-latest}
52+ restart : unless-stopped
53+ depends_on :
54+ postgres :
55+ condition : service_healthy
56+ environment :
57+ NODE_ENV : production
58+ HOST : 0.0.0.0
59+ PORT : 3000
60+ DATABASE_URL : postgresql://${POSTGRES_USER:-dose}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/${POSTGRES_DB:-dose}
61+ CONFIG_PATH : /config
62+ TRANSCODE_PATH : /transcode
63+ TMDB_API_TOKEN : ${TMDB_API_TOKEN:-}
64+ # The Caddy proxy in this compose is the only way in; trust its
65+ # X-Forwarded-For so login throttling sees real client addresses.
66+ TRUST_PROXY : " true"
67+ # Transcoding: `auto` tests every GPU encoder at startup and uses the
68+ # fastest that works, `off` forces software, or name one of nvenc / qsv /
69+ # amf / vaapi / videotoolbox. See docs/performance.md for what it is worth
70+ # and why HWACCEL_DECODE is off unless the CPU is slow.
71+ HWACCEL : ${HWACCEL:-auto}
72+ HWACCEL_DECODE : ${HWACCEL_DECODE:-false}
73+
74+ # A GPU has to be handed to the container before any of it applies. NVIDIA
75+ # needs the container toolkit on the host; Intel and AMD need the render
76+ # node. Uncomment the block that matches this machine.
77+ # deploy:
78+ # resources:
79+ # reservations:
80+ # devices:
81+ # - driver: nvidia
82+ # count: 1
83+ # capabilities: [gpu, video]
84+ # devices:
85+ # - /dev/dri:/dev/dri # Intel Quick Sync or AMD VAAPI
86+
87+ # Reached through the proxy; expose a port directly only if you need to
88+ # bypass it (DOSE_PORT=3000 restores the old behaviour).
89+ ports :
90+ - " ${DOSE_PORT:-127.0.0.1:3000}:3000"
91+ volumes :
92+ - dose-config:/config
93+ - dose-transcode:/transcode
94+ - ${DOSE_MOVIES_PATH:-./media/movies}:/media/movies:ro
95+ - ${DOSE_SHOWS_PATH:-./media/shows}:/media/shows:ro
96+ healthcheck :
97+ test : ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/api/v1/health"]
98+ interval : 10s
99+ timeout : 3s
100+ retries : 5
101+ start_period : 15s
102+ security_opt :
103+ - no-new-privileges:true
104+
105+ postgres :
106+ image : postgres:17-alpine
107+ restart : unless-stopped
108+ environment :
109+ POSTGRES_DB : ${POSTGRES_DB:-dose}
110+ POSTGRES_USER : ${POSTGRES_USER:-dose}
111+ POSTGRES_PASSWORD : ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
112+ volumes :
113+ - dose-postgres:/var/lib/postgresql/data
114+ healthcheck :
115+ test : ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
116+ interval : 5s
117+ timeout : 3s
118+ retries : 10
119+ security_opt :
120+ - no-new-privileges:true
121+
122+ proxy :
123+ image : caddy:2-alpine
124+ restart : unless-stopped
125+ depends_on :
126+ dose :
127+ condition : service_healthy
128+ ports :
129+ - " 80:80"
130+ - " 443:443"
131+ configs :
132+ - source : caddyfile
133+ target : /etc/caddy/Caddyfile
134+ volumes :
135+ - dose-caddy-data:/data
136+ - dose-caddy-config:/config
137+ security_opt :
138+ - no-new-privileges:true
139+
140+ # Reverse proxy for Dose, serving https://dose.local with a locally generated
141+ # certificate. HTTPS is not cosmetic: installing Dose as an app and holding
142+ # offline downloads both require a secure context on iOS.
143+ #
144+ # Point dose.local at this machine first (router DNS entry, or a hosts line
145+ # on each device). The certificate is Caddy's own local CA: trust it once per
146+ # device or accept the browser warning; a LAN name cannot get a public one.
147+ configs :
148+ caddyfile :
149+ content : |
150+ dose.local {
151+ tls internal
152+ encode gzip
153+ # WebSockets (live catalog updates) are proxied transparently.
154+ reverse_proxy dose:3000 {
155+ # Streaming and downloads move gigabytes; never buffer them.
156+ flush_interval -1
157+ }
158+ }
159+
160+ # Plain HTTP redirects to HTTPS so a typed address still lands somewhere.
161+ http://dose.local {
162+ redir https://dose.local{uri} permanent
163+ }
164+
165+ volumes :
166+ dose-postgres :
167+ dose-config :
168+ dose-transcode :
169+ dose-caddy-data :
170+ dose-caddy-config :
171+ ` ` `
172+
173+ **3. Point ` dose.local` at this machine** — a DNS entry on your router, or a
174+ hosts-file line on each device.
175+
176+ **4. Start it**
177+
178+ ` ` ` sh
179+ docker compose pull
180+ docker compose up -d
181+ ` ` `
182+
183+ Open `https://dose.local`. The first visit creates the administrator account.
184+ Updating is the same two commands again.
20185
21186The bundled Caddy proxy serves HTTPS with a local certificate — required for
22187installing Dose as an app and for offline downloads on iOS. The app container
@@ -28,6 +193,12 @@ Dose configuration, temporary transcodes, and PostgreSQL data use the writable
28193named volumes `dose-config`, `dose-transcode`, and `dose-postgres`. The Dose
29194container runs database migrations automatically before starting the server.
30195
196+ To run the code from a checkout instead of the published image :
197+
198+ ` ` ` sh
199+ docker compose -f compose.yaml -f compose.build.yaml up --build -d
200+ ` ` `
201+
31202# # Development
32203
33204Install Bun 1.3.10, then run :
0 commit comments