Self-hosting quickstart
A normal public self-hosted Pinchana deployment has two independently deployed applications:
- Pinchana API — gateway, normal scraper modules, Gluetun, shared media cache, and optional DLP profile.
- Pinchana Web — Next.js BFF/browser UI pointing at that API through server-only configuration.
You can run them on one host or separate hosts. In production, put both behind HTTPS reverse proxies.
1. Deploy Pinchana API
Section titled “1. Deploy Pinchana API”git clone --recurse-submodules https://github.com/Pinchana/pinchana-api.gitcd pinchana-apicp .env.example .envchmod 600 .envConfigure at least production VPN and machine authentication:
WIREGUARD_PRIVATE_KEY=REPLACE_WITH_PROVIDER_WIREGUARD_PRIVATE_KEYGLUETUN_API_KEY=REPLACE_WITH_RANDOM_CONTROL_SECRETPINCHANA_API_KEYS={"operator":"REPLACE_WITH_LONG_RANDOM_MACHINE_KEY"}If Spotify should work, add its client ID/secret.
Resolve the current coherent API release to immutable image digests:
python scripts/update_rolling.py --env-file .env --dry-runpython scripts/update_rolling.py --env-file .envStart and verify:
docker compose --env-file .env config --quietdocker compose --env-file .env up --detachcurl --fail-with-body --silent --show-error http://127.0.0.1:8080/healthExpose only the API gateway through your public reverse proxy, for example https://api.example.com -> http://127.0.0.1:8080. Do not intentionally expose scraper or Gluetun control ports to the Internet.
2. Configure API browser authentication
Section titled “2. Configure API browser authentication”Pinchana Web needs the API’s isolated browser-session flow. Create a Cloudflare Turnstile widget for the final Web hostname and set the API-side private values:
TURNSTILE_SECRET_KEY=REPLACE_WITH_PRIVATE_TURNSTILE_SECRETTURNSTILE_EXPECTED_HOSTNAME=pinchana.example.comTURNSTILE_EXPECTED_ACTION=turnstile-spin-v1TURNSTILE_SESSION_SECRET=REPLACE_WITH_AT_LEAST_32_RANDOM_CHARACTERSRecreate the gateway after changing environment:
docker compose --env-file .env up --detach --no-deps --force-recreate serverThe public Turnstile site key is used by Web; the private secret stays on API.
3. Optional Shorts cookies
Section titled “3. Optional Shorts cookies”For YouTube Shorts that require authenticated yt-dlp access, place Netscape-format cookie files under the directory configured by SHORTS_COOKIES_DIR (default ./secrets/yt-cookies).
For rootful Docker and the current root-running Shorts container:
sudo install -d -o root -g root -m 0700 secrets/yt-cookiessudo chown root:root secrets/yt-cookies/cookies.txtsudo chmod 0600 secrets/yt-cookies/cookies.txtThe directory is mounted read-only at /run/pinchana-cookies. With rootless Docker, own it with the host user running Docker instead. Never commit cookies.
4. Deploy Pinchana Web
Section titled “4. Deploy Pinchana Web”In a separate checkout:
git clone https://github.com/Pinchana/pinchana-web.gitcd pinchana-webcp .env.example .envchmod 600 .envSet:
PINCHANA_API_URL=https://api.example.comNEXT_PUBLIC_TURNSTILE_SITE_KEY=REPLACE_WITH_PUBLIC_SITE_KEYSENTRY_MONITORING_ENABLED=falsePINCHANA_API_URL is server-only and must be HTTPS in production. The site key must be the public key corresponding to the API’s Turnstile secret and final Web hostname.
Validate/build/start:
docker compose --env-file .env config --quietdocker compose --env-file .env up --detach --builddocker compose --env-file .env psBy default Web binds to 127.0.0.1:3000. Reverse-proxy your public Web origin, for example https://pinchana.example.com -> http://127.0.0.1:3000.
5. Custom-instance selector (optional)
Section titled “5. Custom-instance selector (optional)”A self-hosted Web deployment can point directly at its configured PINCHANA_API_URL without an instance certificate.
Configure PINCHANA_INSTANCE_PUBLIC_KEY only if you want that Web deployment to let users connect to additional project-certified custom API origins. The project signing private key must never be deployed to Web or API.
6. Optional DLP
Section titled “6. Optional DLP”Ordinary YouTube/youtu.be downloads in Web require DLP protocol v2. Keep it disabled until the internal profile is deployed and canaried:
DLP_ENABLED=falsePin DLP images with the same coherent release:
python scripts/update_rolling.py --env-file .env --dlp --dry-runpython scripts/update_rolling.py --env-file .env --dlpThen follow DLP operations and security. Do not simply flip DLP_ENABLED=true before the preflight and canary succeed.
7. Final checks
Section titled “7. Final checks”From outside the hosts, verify:
- the API health endpoint is reachable over HTTPS;
- the Web page loads over HTTPS;
- Turnstile successfully establishes a browser session;
- one normal supported URL processes and previews media;
- protected media is not anonymously reusable from the API origin;
- if DLP is enabled,
/web/capabilitiesreports protocol 2 and a production YouTube smoke download succeeds.
Keep .env, cookies, VPN credentials, and all session/DLP secrets outside source control.