Dockerized FastAPI service that downloads videos from Instagram, YouTube, TikTok, and 1000+ sites via yt-dlp, then uploads to IPFS via Pinata.
- Multi-Platform Support: Instagram, YouTube, TikTok, and more via yt-dlp
- Cookie Authentication: Instagram authentication to bypass rate limits
- IPFS Integration: Automatic Pinata upload with CID generation
- RESTful + Slug API: JSON POST and base64 URL slug support
- Health Monitoring: Cookie validation and expiration tracking
- File Management: Configurable retention and size limits (max 1.5GB)
- Docker / Docker Compose
- Pinata account + JWT (Settings → API Keys → Create JWT)
- Instagram cookies (for Instagram downloads) - See Cookie Management section
- Optional: Tailscale on the host for private access
cp .env.example .envand fillPINATA_JWT- Add Instagram cookies to
data/instagram_cookies.txt(see Cookie Management) docker compose up -d --build- Hit
http://<host-or-tailnet-ip>:6666/healthz(external port)
Note on Ports:
- Internal Port:
8000(FastAPI app inside container) - External Port:
6666(exposed on host machine) - Access via
http://localhost:6666on the host
Download content from URL and upload to IPFS.
curl -X POST "http://<ip>:6666/download" \
-H 'content-type: application/json' \
-d '{"url":"https://www.instagram.com/p/ABC123/"}'Alternative slug-based endpoint.
# Make slug
python3 - <<'PY'
import base64
u = "https://www.instagram.com/p/ABC123/"
print(base64.urlsafe_b64encode(u.encode()).decode().rstrip('='))
PY
# Call
curl "http://<ip>:6666/d/<slug>"Service health check with cookie status.
curl "http://<ip>:6666/healthz"Response:
{
"status": "ok",
"timestamp": "2025-12-05T10:35:00Z",
"authentication": {
"cookies_enabled": true,
"cookies_exist": true,
"cookies_valid": true,
"last_validation": "2025-12-05T10:00:00Z",
"cookies_path": "/data/instagram_cookies.txt"
},
"version": "2.0.0"
}Validate Instagram cookie authentication.
curl -X POST "http://<ip>:6666/cookies/validate"Check cookie expiration status.
curl "http://<ip>:6666/cookies/status"{
"status": "ok",
"cid": "bafy...",
"ipfs_uri": "ipfs://bafy...",
"pinata_gateway": "https://ipfs.skatehive.app/ipfs/bafy...",
"filename": "instagram_video_ABC123.mp4",
"bytes": 14839234,
"source_url": "https://www.instagram.com/p/ABC123/"
}Instagram requires authentication to download content. The service uses browser cookies to authenticate as a logged-in user.
Cookies must be in Netscape format and stored in data/instagram_cookies.txt:
# Netscape HTTP Cookie File
.instagram.com TRUE / TRUE 1234567890 csrftoken ABC123...
.instagram.com TRUE / TRUE 1234567890 sessionid XYZ789...
Method 1: Browser Extension (Recommended)
- Install "Get cookies.txt LOCALLY" extension (Chrome/Firefox)
- Log into Instagram in your browser
- Navigate to instagram.com
- Click extension icon → Export → Netscape format
- Save as
data/instagram_cookies.txt
Method 2: Browser DevTools
- Log into Instagram
- Open DevTools (F12) → Application/Storage → Cookies
- Find
sessionidandcsrftokencookies - Create Netscape format file manually
When to Refresh:
- Cookie expiration warning from
/healthzendpoint - "Rate limit" or "Login required" errors
- Service returns authentication errors
- Every 6-12 months (Instagram cookie lifetime)
Refresh Steps:
-
Obtain fresh cookies (see above methods)
-
Update cookie file:
# On Mac Mini M4 or Raspberry Pi cd skatehive-monorepo/skatehive-instagram-downloader/ytipfs-worker nano data/instagram_cookies.txt # paste new cookies
-
Restart service:
docker compose restart
-
Verify:
curl http://localhost:6666/healthz curl -X POST http://localhost:6666/cookies/validate
- Never commit
instagram_cookies.txtto git (already in .gitignore) - Use cookies from a dedicated Instagram account if possible
- Rotate cookies periodically
- Monitor for unusual activity on the Instagram account
Current Live Configuration:
- External URL:
https://minivlad.tail83ea3e.ts.net/instagram/download - External Port:
6666 - Internal Port:
8000 - Container:
ytipfs-worker - Upload Limit:
1500MB - Cookie File:
data/instagram_cookies.txt - Network: Tailscale Funnel (publicly accessible)
Port Mapping:
# docker-compose.yml
ports:
- "6666:8000" # Host:ContainerPINATA_JWT(required)DOWNLOAD_DIRdefault/dataYTDL_FORMATdefaultbv*+ba/bestvideo+bestaudio/bestOUTPUT_TEMPLATEdefault%(title).80s-%(id)s.%(ext)sMAX_FILE_MBdefault1500KEEP_FILESdefault0(delete file after pin)
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh
tailscale ip -4 # use 100.x.x.x hereCause: Instagram cookies are expired or invalid
Solution:
- Check cookie status:
curl http://localhost:6666/healthz - Refresh cookies (see Cookie Management section)
- Restart service:
docker compose restart - Verify:
curl -X POST http://localhost:6666/cookies/validate
Cause: Service not accessible or port misconfiguration
Solution:
- Check service is running:
docker ps | grep ytipfs - Test locally:
curl http://localhost:6666/healthz - Check Tailscale Funnel:
tailscale funnel status - Verify port mapping in docker-compose.yml (should be 6666:8000)
Cause: Video exceeds MAX_FILE_MB limit (default 1500MB)
Solution:
- Increase limit in .env:
MAX_FILE_MB=2000 - Restart service:
docker compose restart - Consider storage implications
Cause: Invalid URL, private post, or cookie issue
Solution:
- Verify URL format:
https://www.instagram.com/p/<POST_ID>/ - Check if post is public
- Validate cookies:
curl -X POST http://localhost:6666/cookies/validate - Try different Instagram URL format
Cause: Missing PINATA_JWT or cookie file
Solution:
- Check .env file exists and has PINATA_JWT
- Ensure data/instagram_cookies.txt exists
- Check logs:
docker compose logs ytipfs-worker - Verify file permissions on data/ directory
- ffmpeg is installed in the container; yt-dlp merges to mp4 when possible.
- Large files are blocked by
MAX_FILE_MB. - Set
KEEP_FILES=1to retain downloads for debugging. - Cookie file location:
ytipfs-worker/data/instagram_cookies.txt - Backup cookie file location:
ytipfs-worker/data/instagram_cookies_real.txt