A home media server using docker-compose
that enables SSL wildcard certificates via Cloudflare and LetsEncrypt. The approach allows layering of complexity and features including Single sign on (SSO) via GCP.
NOTE: These are listed in the order I recommend they are layered and tested during setup.
traefik-cloudflare.yml
and optional traefik-oauth.yml
- Traefik - Service ingress, routing, etc, handles SSL certificates via DNS-01 challenge and LetsEncrypt
misc.yml
and optional misc-oauth.yml
- Watchtower - A process for automating Docker container base image updates
- Duplicati - Backup software to store encrypted backups
- Portainer
multimedia.yml
, optional multimedia-oauth.yml
- Prowlarr - quick start - Indexer/manager manager/proxy that supports management of both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Lidarr, Radarr, Readarr, and Sonarr offering complete management of your indexers with no per app Indexer setup required. HINT: use local network names in settings e.g. http://prowlarr:9696, http://radarr:7878, http://sonarr:8989, http://sabnzbd:8080.
- SABnzbd - Usenet downloader. Use top level
DATA
env variable to avoid needing to specify "Remote Path" it the *arr applications. This makes it easier to hardlink. See #Layered setup stage 2 - Sonarr - should pull indexer from prowlarr
- Radarr - should pull indexer from prowlarr
- Plex - NOTE: in initial config, connect to
http://<my-local-ip>:32400/web
first to complete the setup wizard. Plex differs from every other container and useshost
networking, therefore enableRemote
access, port forward32400
to<my-local-ip>
and then you may access remotely via https://app.plex.tv. Usinghost
networking makes configuration easier, and ensures traffic runs unrestricted and local devices can easily discover the server. The not-so-big-deal-downside is that plex.example.com no longer is your go-to url. - Organizr - Organize a single page with tabs to manage all services
NOTE: updates likely needed here.
- Fork this repo (so you can keep changes if you need to)
- Clone your forked repo
cd mediabox
cp .env.template .env
- Replace variables on
.env
with whatever makes sense to you (follow the comments above each property). Don't worry, start with the basics and get to the rest later
NOTE: set up duplicati.example.com later to make sure your repo and specifically your .env
is backed up daily.
Setting up a home server can be complex. For all examples, we will assume you are using the domain example.com
. You usually need to setup your domain on cloudflare, forward ports on your router and set up dynamic dns. Before attempting to configure additional applications, this repo is setup to allow you to layer in complexity by minimizing containers started using the COMPOSE_FILE
variable. The simple shell script wraps the docker-compose
command to make it simple to execute up
, down
, restart
, logs
, shell
and most any other typical docker-compose
command (via cmd
). See Usage or simply execute ./mb
to see it.
Successful completion of this stage means you can access both https://traefik.example.com and https://whoami.example.com, with optional IP whitelist restriction and/or optional Single Sign On.
- Set up cloudflare DNS for your example.com domain
- Set up dynamic DNS to cloudflare to update the
A
record for your example.com - Add wildcard CNAME - Add a
* CNAME
to@
- Increase cloudflare security - Configure increased security on your cloudflare zone, see Cloudflare Settings for Traefik Docker: DDNS, CNAMEs, & Tweaks
- Verify HTTPS connectivity - In
.env
setCOMPOSE_FILE=traefik-cloudflare.yml
,DOMAIN,SSL_ACME_EMAIL,CF_API_EMAIL,CF_API_KEY
and./mb up
- verify connectivity to both https://traefik.example.com and https://whoami.example.com. Check logs with./mb logs traefik
and make sure there are no errors../mb down
when done. - (optional) Restrict IPs allowed - Restrict your
IP_WHITELIST_SOURCERANGE
in.env
to a minimal set of IP addresses, or if you want it public, leave it open by default0.0.0.0/0
- (optional) Setup Single Sign On - Set
COMPOSE_FILE=traefik-cloudflare.yml:traefik-oauth.yml
in.env
and./mb up
- set up GCP based SSO via OAUTH. Some background is available here on the external steps, but as-is you only need to make sure your associated ENV variables are populated. Now verify the auth challenge to both https://traefik.example.com and https://whoami.example.com../mb down
when done.
All good? If not do not continue.
Plan your disk layout and set up your NFS (or other) disk mounts (beyond the scope of this readme). This setup follows best practices mentioned on this article and this reddit post to be able to use hardlinks and/or perform atomic move
operations instead of copy+delete
(which takes longer and requires more space).
TL;DR from Trash guides "how to set up for docker":
The paths you use on the inside matter. Because of how Dockerβs volumes work, passing in two or three volumes such as the commonly suggested /tv, /movies and /downloads makes them look like two or three file systems, even if they arenβt. This means hard links wonβt work and instead of an instant move, a slower and more I/O intensive copy + delete is used.
# My disks layout:
#
# βββ nas
# βΒ Β βββ mediabox (mounted as /data)
# | βββ backups
# | βββ torrents
# βΒ Β | βββ movies
# βΒ Β | βββ pictures
# βΒ Β | βββ tv
# | βββ usenet
# βΒ Β | βββ movies
# βΒ Β | βββ pictures
# βΒ Β | βββ tv
# | βββ media
# βΒ Β βββ movies
# βΒ Β βββ pictures
# βΒ Β βββ tv
# βββ ssd
# βββ containers
# βββ repo
#
# CONTAINERS : Application docker container storage - SSD recommended. Intend to backup this to the BACKUPS.
# DATA : This directory will be the top level mount point mounted in containers. Configure relative paths inside the applications.
# BACKUPS : Target location for backups (can also use online services instead inside duplicati)
#
CONTAINERS=/containers
DATA=/mnt/nfs/mediabox
BACKUPS=/mnt/nfs/mediabox/backups
Now it is time to determine what services you want to run.
- Add primary applications - Edit your
.env
and chainmediabox.yml
on to yourCOMPOSE_FILE
variable. The same as above,./mb up
then check the logs of the various containers like./mb logs plex
. You should now be able to access each by name e.g. https://plex.example.com. - (optional) Protect applications with SSO - Edit your
.env
and chainmediabox-oauth.yml
on to yourCOMPOSE_FILE
variable. Test again, but this time use an incognito browser (or other browser that has not utilized SSO in steps above) and be sure you receive a challenge for https://plex.example.com - (optional) Torrents, books, etc - view the remainder of the compose files and see what services you want. Using the same methodology, chain the compose file, and test your changes. (I am not using torrents or books, so these may need adjustments, feel free to PR changes)
usage: mb [help|-h|--help] <subcommand>
optional arguments:
help | -h | --help
print this message and exit
subcommands:
start
starts the configured files
stop
stops
restart
restarts, a combination of stop and start
update
pulls updated images
shell
open a shell to the targeted container
logs
shows logs
docompose <command>
executes any arbitrary docker compose command. Use "docompose help" to list them
The logs subcommand can be appended by flags and specify the container(s). example:
mb logs -f --tail 500 plex
shows logs only for plex service
Be sure to run commands either with sudo, or as a user who is part of the "docker" group
Watchtower automatically updates all apps (if docker image update is available) at 4 AM every day.
With OpenVPN you can use any VPN provider following these steps:
- Download your VPN OpenVPN config files (e.g: NordVPN TCP/UDP config files)
- Download your VPN CA file (e.g: NordVPN CA & TLS key files)
- Run the following (using NordVPN Brazil#65 as example)
# Copy required files
cp ~/Downloads/br65.nordvpn.com.udp.ovpn ${OPENVPN}/vpn.conf
cp ~/Downloads/br65_nordvpn_com_ca.crt ${OPENVPN}/vpn-ca.crt
# Write credentials
cat <<EOT >> ${OPENVPN}/vpn.auth
you@mail.com
YourVPNP4ssw0rD
EOT
Much of this repo was developed by the original other and contributors at https://github.com/cristianmiranda/mediabox. I did not choose to PR my changes because the repo was not very active. While a PR from this repo could be created and applied upstream, I did not want to take the time to justify choices I made including some structural changes. If someone wants to do that work to push this upstream, I welcome getting my changes contributed to the original body of work.
Anyway, thanks to @christianmiranda and other contributors from the original repo.