MeTube
Container
Web GUI for youtube-dl (using the yt-dlp fork) with playlist support. Allows you to download videos from YouTube and dozens of other sites (https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md)
Image details
Source details
Configuration
TypeContainerlinuxalexta69/metube:latest8081:8081/tcp/downloads : /portainer/DownloadsPUID=1000PGID=1000unless-stoppedTemplate by novaspirit
Report issueNotes
Check our Github page: https://github.com/pi-hosted/pi-hosted
Official Webpage: https://hub.docker.com/r/alexta69/metube
Official Docker Documentation: https://github.com/alexta69/metube
Standalone Install
Select an install method, to see config/commands for deploying MeTube
Install on Portainer
Import all app templates into your Portainer instance, for easy 1-click deploys
- Ensure both Docker and Portainer are installed, and up-to-date
- Log into your Portainer web UI
- Under Settings → App Templates, paste the below URL
- Head to Home → App Templates, and the list of apps will show up
- Select MeTube, fill in any config options, and hit Deploy
Template Import URL
https://raw.githubusercontent.com/Lissy93/portainer-templates/main/templates.json
Show Me
More install options in our documentation, or see yt-dlp/yt-dlp for app-specific guidance.
MeTube
MeTube is a self-hosted web UI for yt-dlp. Paste a link from YouTube or hundreds of other sites, and it downloads to your server:
- Video, audio, subtitles and thumbnails — from single videos, whole playlists or entire channels.
- Subscriptions to channels and playlists, which queue new uploads as they appear.
- Per-download choices of quality, format and folder, plus server-wide yt-dlp options and presets.
📖 Guides, recipes and a troubleshooting FAQ live in the wiki — many feature requests are already a recipe there, so check it before filing one.

Quick start
docker run -d -p 8081:8081 -v /path/to/downloads:/downloads ghcr.io/alexta69/metubeOr with Docker Compose:
services:
metube:
image: ghcr.io/alexta69/metube
container_name: metube
restart: unless-stopped
ports:
- "8081:8081"
volumes:
- /path/to/downloads:/downloadsThen open
http://<host>:8081 in your browser. Images are multi-arch (amd64/arm64), and also published on Docker Hub as alexta69/metube.Configuration
MeTube is configured with environment variables:-e NAME=value on the docker run command line, or the environment: section in Compose. Defaults are the Docker image's; outside Docker, the directories default to the working directory.Runtime and permissions
| Variable | Default | Description |
|---|---|---|
PUID / PGID | 1000 | User and group MeTube runs as and writes files with. Legacy UID/GID also work. |
UMASK | 022 | Umask for the files MeTube creates. |
CHOWNDIRS | true | Make PUID:PGID the owner of the download, state and temp directories at startup. With false, MeTube's user must already have access. |
LOGLEVEL | INFO | DEBUG, INFO, WARNING, ERROR, CRITICAL or NONE. |
ENABLEACCESSLOG | false | Log every HTTP request. |
DEFAULTTHEME | auto | UI theme: light, dark, or auto to follow the system. |
Downloads
| Variable | Default | Description |
|---|---|---|
MAXCONCURRENTDOWNLOADS | 3 | Downloads that run at once; the rest wait their turn. |
DEFAULTOPTIONPLAYLISTITEMLIMIT | 0 | Default for the Items Limit field: how many entries of a playlist or channel to download (0 = all). |
CLEARCOMPLETEDAFTER | 0 | Seconds before finished and failed downloads leave the Completed list (0 = never). |
DELETEFILEONTRASHCAN | false | Also delete the file from disk when its entry is removed from Completed. ask prompts each time. |
SUBSCRIPTIONDEFAULTCHECKINTERVAL | 60 | Default minutes between checks of a subscription. |
SUBSCRIPTIONSCANPLAYLISTEND | 50 | Newest entries fetched each time a subscription is checked. |
SUBSCRIPTIONMAXSEENIDS | 50000 | Video IDs remembered per subscription, to bound the state file's size. |
Directories
| Variable | Default | Description |
|---|---|---|
DOWNLOADDIR | /downloads | Where downloads are saved. |
AUDIODOWNLOADDIR | same as DOWNLOADDIR | Where audio-only downloads are saved, to keep them apart from video. |
TEMPDIR | /downloads | Where files are written while downloading. An SSD or tmpfs is faster, but on a RAM disk interrupted downloads can't resume. |
STATEDIR | /downloads/.metube | Where MeTube keeps its queue, history, subscriptions and uploaded cookies. |
CUSTOMDIRS | true | Show a Download Folder field under Advanced Options, to save into a subfolder of the download directory. |
CREATECUSTOMDIRS | true | Let that field create folders that don't exist yet. |
CUSTOMDIRSEXCLUDEREGEX | (^\</td><td>/)<a href="undefined">.@</a>.<em>$ | Folders left out of the field's suggestions; the default hides names starting with . or @. Empty hides none. |
DEFAULTFOLDER | Folder the field starts with, relative to the download directory. Requires CUSTOMDIRS. | |
DOWNLOADDIRSINDEXABLE | false | Serve browsable listings of the download directories. |
File naming
Templates use yt-dlp's output template syntax. How MeTube applies them is explained in Output templates.| Variable | Default | Description |
|---|---|---|
OUTPUTTEMPLATE | %(title)s.%(ext)s | Filename for downloads. |
OUTPUTTEMPLATEPLAYLIST | %(playlisttitle)s/%(title)s.%(ext)s | Filename for items added from a playlist. Empty means OUTPUTTEMPLATE. |
OUTPUTTEMPLATECHANNEL | %(channel)s/%(title)s.%(ext)s | Filename for items added from a channel. Empty means OUTPUTTEMPLATE. |
OUTPUTTEMPLATECHAPTER | %(title)s - %(sectionnumber)02d - %(sectiontitle)s.%(ext)s | Default filename for each chapter when Split by chapters is on. |
yt-dlp
| Variable | Default | Description |
|---|---|---|
YTDLOPTIONS | {} | Options for every download, as a JSON object — see yt-dlp options. |
YTDLOPTIONSFILE | Path to a JSON file of such options. Reloaded when it changes; wins over YTDLOPTIONS for the same key. | |
YTDLOPTIONSPRESETS | {} | Named option bundles, picked per download in the UI. |
YTDLOPTIONSPRESETSFILE | Path to a JSON file of presets. Reloaded when it changes; wins over YTDLOPTIONSPRESETS for the same name. | |
ALLOWYTDLOPTIONSOVERRIDES | false | Show a field for per-download yt-dlp options in the UI. Trusted users only: it allows running commands in the container. |
YTDLNIGHTLYUPDATETIME | Use nightly yt-dlp builds, upgrading and restarting daily at this time (HH:MM, 24-hour). |
Web server and network
| Variable | Default | Description |
|---|---|---|
HOST | 0.0.0.0 | Address to listen on. The default is every IPv4 interface; (or empty) adds IPv6; :: is IPv6 only. |
PORT | 8081 | Port to listen on. |
URLPREFIX | / | Base path, for serving MeTube under a subpath of a reverse proxy. |
HTTPS | false | Serve HTTPS directly, using CERTFILE and KEYFILE. |
CERTFILE / KEYFILE | Paths to the HTTPS certificate and key. | |
PUBLICHOSTURL | download/ | Base URL for the links to completed files. The default is relative to MeTube itself; set a full URL if the files are served from elsewhere. |
PUBLICHOSTAUDIOURL | audiodownload/ | The same, for audio downloads. |
CORSALLOWEDORIGINS | Comma-separated origins allowed to call MeTube from other sites, as browser extensions and bookmarklets do. allows any site, so prefer naming them. | |
ALLOWPRIVATEADDRESSES | false | Allow URLs on private and internal addresses, turning off SSRF protection. Needed for Fake-IP proxy clients, not for ordinary proxies — details. |
ROBOTSTXT | Path to a robots.txt file to serve, mounted into the container. |
yt-dlp options
yt-dlp options are JSON objects keyed by yt-dlp's API option names, which differ from its command-line flags (converter). They apply at three levels, and for an option set at more than one, the more specific level wins:- Global —
YTDL_OPTIONSandYTDL_OPTIONS_FILE, for every download. - Presets —
YTDL_OPTIONS_PRESETSandYTDL_OPTIONS_PRESETS_FILE, picked per download under Advanced Options. - Overrides — typed into the UI for a single download, when
ALLOW_YTDL_OPTIONS_OVERRIDESis on.
environment:
- 'YTDL_OPTIONS={"writesubtitles": true, "subtitleslangs": ["en", "de"], "updatetime": false}'The yt-dlp options guide covers presets, overrides and option files in detail, and the YTDLOPTIONS Cookbook has ready-made recipes.
Guides
The wiki covers everything beyond the reference above:- Subscriptions — download new uploads from channels and playlists automatically.
- yt-dlp options and the YTDLOPTIONS Cookbook — notifications, embedded metadata, subtitles and more.
- Output templates and the OUTPUTTEMPLATE Cookbook — how files are named and sorted into folders.
- Using browser cookies — for private, age-restricted and "confirm you're not a bot" videos.
- Sending links to MeTube — browser extensions, bookmarklets, iOS and Android apps, Raycast.
- HTTPS and reverse proxies — NGINX, Apache, Caddy and swag examples, and adding authentication.
- Hardware-accelerated transcoding — re-encode downloads on an Intel or AMD GPU.
- yt-dlp plugins — extend yt-dlp, e.g. to accept links from Invidious or Piped.
- Troubleshooting FAQ — common problems and how to diagnose them.
Keeping yt-dlp up to date
Sites change constantly, and yt-dlp keeps up with frequent releases. A new MeTube image is published automatically for every yt-dlp stable release, so keep your container updated — watchtower can do it for you. To follow yt-dlp's nightly builds instead, setYTDL_NIGHTLY_UPDATE_TIME.Troubleshooting and support
MeTube is only a UI for yt-dlp, so most download failures are yt-dlp's. Reproduce them with yt-dlp inside the container, and once a set of options works there, carry it over toYTDL_OPTIONS:docker exec -ti metube sh
cd /downloads
yt-dlp <url>The Troubleshooting FAQ covers common problems. Ask questions in Discussions; issues are for bugs in MeTube itself.
Scope and contributing
MeTube's scope is deliberately narrow: it downloads well, and stops once the file is written. Tagging and library organization belong to dedicated tools — point beets (headless, scriptable), MusicBrainz Picard (GUI, acoustic fingerprinting) or Lidarr (full library manager) at yourAUDIO_DOWNLOAD_DIR.Feature requests that fit this scope are welcome, and so are pull requests — for a change that isn't obvious, start a discussion first. CONTRIBUTING.md has the scope policy and how to build and run MeTube locally.
Serve MeTube on your own domain behind Caddy, Nginx or Traefik. Fill in your domain and copy the result. It's a starting point, some apps need their own base URL or extra headers set too.
Proxying metube.example.com to http://MeTube:8081
Add this to your Caddyfile
metube.example.com {
reverse_proxy http://MeTube:8081
}Check the logs first
Nine times out of ten the logs tell you exactly what went wrong.
- In Portainer, go to Containers, click the container, then Logs. Or run
docker logs MeTube - Exit codes help too:
137means killed, usually out of memory.126or127means the command inside the image is broken.
Port already in use
If deployment fails with "Bind for 0.0.0.0:8081 failed: port is already allocated", something else on your server is using that port.
- Find what's using it:
sudo ss -tlnp | grep :8081 - Stop the other service, or pick a different host port. In
8081:8081only the left number is yours to change, the right one belongs to the app.
Running but the page won't load
The container is up but nothing appears in your browser.
- Use your server's real IP:
http://your-server-ip:8081. The 0.0.0.0 link Portainer shows isn't a real address. - Give it a minute after first deploy, MeTube can take a while to initialise.
- Make sure your firewall allows the port, e.g.
sudo ufw allow 8081
Permission denied on volumes
If the logs show "permission denied", the app can't write to its data folder on the host.
- Fix the ownership:
sudo chown -R 1000:1000 /portainer/Downloads - Or set the
PUIDandPGIDvariables (defaults 1000:1000) to match your own user, found withid $USER
Image won't pull
Test the pull directly on the host: docker pull alexta69/metube:latest
- "manifest unknown" means the tag no longer exists. This template uses
latest, so try pinning a specific version instead. - "toomanyrequests" is the Docker Hub rate limit. Log in with
docker loginto raise it. - "no space left on device" means a full disk. Reclaim space with
docker system prune
"exec format error"
This means the image was built for a different CPU architecture than your server.
- This image supports:
amd64, arm64 - Check yours with
uname -m: x86_64 is amd64, aarch64 is arm64. Raspberry Pi and other ARM boards are the usual culprits.
Container keeps restarting
The unless-stopped restart policy relaunches the app after every crash, so the real error can scroll past.
- Check the logs right after a restart, the last few lines before it died are the useful ones.
- Get the exit code with
docker inspect MeTube --format '{{.State.ExitCode}}' - Still stuck? Redeploy once with the restart policy set to
noso the failure stays visible.
Raise an issue
Found something which isn't working as it should? Here's how to report it.
- Bug within the app: Open an issue on yt-dlp/yt-dlp
- Template not working: Open an issue on novaspirit/pi-hosted
- This website not working: Open an issue on lissy93/portainer-templates
A single container
MeTube runs as one container, the simplest kind of app here. Just the one image to pull and nothing else wired up alongside it.
The app image
An image is the app packed up ready to go, everything MeTube needs bundled into one download. This template pulls alexta69/metube:latest, which Docker fetches once (about 304 MB) and then starts your own copy from.
Where the image comes from
Docker pulls its images from registries, public libraries of ready-built apps. MeTube's comes from Docker Hub, published by alexta69.
Version tags
The bit after the colon in the image name is the version tag. Here it's latest, which always points at the newest build, so a redeploy can bump you to a newer release without you asking. Newest right now is 2026.09.29. Pin a specific tag if you would rather stay on one version.
Which machines it runs on
Every image is built for particular CPU types. This one ships for amd64, arm64, so it runs on both regular x86 servers and ARM boards like a Raspberry Pi.
Ports
A port is the door the app answers on. A mapping like 8081:8081 means it's reachable on port 8081 of your server, where the left number is yours to change and the right one belongs to the app. It opens:
8081:8081
Volumes
A volume is where MeTube keeps its files so they survive an update or a restart. Without one, anything it saves would sit inside the container and vanish the moment it's recreated. This template mounts:
/downloadsfrom/portainer/Downloadson the host
Environment variables
Environment variables are the settings you hand over when you deploy, things like a password or a timezone. MeTube takes 2 of them, all with defaults you can leave alone or tweak:
PUID, defaults to1000PGID, defaults to1000
Restart policy
The restart policy here is unless-stopped, so Docker restarts MeTube after a crash or reboot, but leaves it off when you stop it on purpose. You can change this on the deploy screen. The choices are no (never restart), on-failure (only after a crash), unless-stopped (restart unless you stop it), and always (bring it back no matter what).
Users and permissions
The PUID and PGID settings tell it which user and group to act as on your host. Point them at your own account (find yours with id $USER) so the files it writes into your mounted folders come out owned by you rather than root.
Networking
Nothing custom is set, so MeTube sits on Docker's default bridge network: its own private space that reaches the outside world only through the ports it publishes.
Container name
Once it's deployed, Portainer names the container MeTube. That's what you'll spot in the containers list and use in commands like docker logs MeTube.
Platform
The platform is linux, the kind of system the container is built to run on. Docker and Portainer handle this on a normal Linux server.
Open source license
MeTube is open source, released under the Unlicense license. In plain terms the code is out in the open, so you're free to run it and change it to fit what you need.
Portainer app templates
Zooming out, this whole page comes from a Portainer app template: a short recipe telling Portainer how to set MeTube up. Add the template list to Portainer once, then deploying MeTube is a click rather than a wall of config.