A self-hosted web UI for downloading files from URLs, uploading local files, or pulling torrents — all saved to folders on your machine.
Deploy once on your homelab or NAS. Download from anywhere, any format.
Paste any HTTP/HTTPS URL. The server fetches the file server-side and saves it directly to your configured folder.
Drag-and-drop or browse to upload files directly from your browser. Configurable size limit up to 10gb by default.
Paste a magnet link or drop a .torrent file. Supports multi-file torrents with live speed and peer count display.
Real-time progress bar with bytes downloaded, total size, and percentage. No page reload needed — auto-refreshes in place.
Configure any number of named folders via DOWNLOAD_FOLDERS. Map each to a volume on your NAS or local disk.
Set APP_PASSWORD for 8-hour sessions. The /admin page shows all jobs with live status and cancel controls.
No build step. Add one service to any existing
docker-compose.yml.
$ docker pull ghcr.io/whispyy/local-link-downloader:latest
docker-compose.ymlservices:
web-downloader:
image: ghcr.io/whispyy/local-link-downloader:latest
restart: unless-stopped
ports:
- "3000:3000"
environment:
DOWNLOAD_FOLDERS: "images:/downloads/images;videos:/downloads/videos"
ALLOWED_EXTENSIONS: ".jpg,.png,.gif,.zip,.mp4,.pdf"
APP_PASSWORD: "your_secret_password"
volumes:
- /mnt/nas/images:/downloads/images
- /mnt/nas/videos:/downloads/videos
- ./logs/web-downloader:/app/logs
$ docker compose up -d
Open http://localhost:3000 — done!
Updating — pull and restart in one line:
$ docker compose pull web-downloader && docker compose up -d web-downloader
All settings are environment variables. No config files, no YAML schemas.
| Variable | Default | Description |
|---|---|---|
DOWNLOAD_FOLDERS |
— | Semicolon-separated key:/path pairs. E.g. images:/mnt/img;tmp:/mnt/tmp |
ALLOWED_EXTENSIONS |
all | Comma-separated list of permitted extensions. E.g. .jpg,.png,.mp4. Does not apply to torrents. |
APP_PASSWORD |
unset | When set, requires a password prompt before access. |
SESSION_TTL_HOURS |
168 | How long an authenticated session stays valid, in hours (default 7 days). |
ENABLE_TRANSCODING |
false | Set to true to enable on-the-fly video transcoding via FFmpeg. |
MAX_UPLOAD_SIZE |
10gb | Max size for direct browser uploads. Accepts b, kb, mb, gb. |
API_PORT |
3000 | Port the Express server listens on inside Docker. |
LOG_DIR |
./logs | Directory where downloads.log and usage data are written. |
DISCORD_WEBHOOK_URL |
unset | Discord webhook URL for download/upload notifications. |
DISCORD_ERROR_WEBHOOK_URL |
unset | Separate Discord webhook for error-level notifications. |
How files land on your host machine rather than inside the container.
Files are downloaded inside the container to paths defined in
DOWNLOAD_FOLDERS.
Bind-mount a host directory to the same container path to make files appear on your machine.
# The container-path is the bridge — it must match on both sides
The container path (the right side of the colon in volumes:) must exactly match what you set in DOWNLOAD_FOLDERS.
Automatically built and pushed on every commit and version tag.
:latest
Published on every push to main. Always points to the newest build.
:v1.2.3
Immutable version tag. Pin to this in production for stable deployments.
:v1.2
Minor-version tag — always points to the latest patch of that minor.
:sha-abc1234
Unique short-SHA tag for every build. Useful for debugging exact versions.