Hands-Off Self-Hosting with Docker & Tailscale - an Introduction

· cyclicircuit's blog

The first in a series of articles describing an effective self-hosting homelab configuration using docker, Tailscale, and dockge.

Table of Contents

Self-hosting is having a bit of a moment right now. Enabled by the proliferation of a series of different technologies, people can now self-host a wide array of web applications with way less effort than what was required previously.

I've been self-hosting for a long time now, and I have selected a series of technologies that I believe can be used together to make a homelab server that is reasonably secure, self-updating, and should require minimal interaction beyond the initial setup. This is what I refer to as a "hands-off self-hosting setup".

As of right now, the following pages are part of this series:

Who is this for? #

This is a series of blog posts intended for people who want to self-host a server with a selection of applications that they're interested in, but are also not interested in spending their time maintaining such a system. The goal is to be able to install self-hosted web-applications almost as easily as one would install programs on a personal computer and keeping them up-to-date automatically.

There is an expectation that people understand the basics of using Linux and the terminal. You should understand how to use commands like cd and ls, you should be able to connect to a machine using ssh, and you should know how to install Ubuntu Server Edition on a machine. You should also understand what an IP address is, and what ports are.

You do not need to know what docker is, or how to use it, I will explain that as part of this series.

What's the Final Result Going to be? #

                          _______ _     _ _______
                             |    |_____| |______
                             |    |     | |______

        _____ __   _ _______ _______  ______ __   _ _______ _______
          |   | \  |    |    |______ |_____/ | \  | |______    |
        __|__ |  \_|    |    |______ |    \_ |  \_| |______    |

                                     △
         ._________________.         ║
         |.---------------.|         ║
         ||               ||         ║        ╔ ═ ═ ═ ═ ═ ═ ═ ╗
         ||   -._ .-.     ||         ║         Made Accessible
         ||   -._| | |    ||         ║        ║to the Internet║
         ||   -._|"|"|    ||         ║          via Tailscale
         ||   -._|.-.|    ||         ║        ║    Funnel     ║
         ||_______________||         ╚══════════════════════════╗
         /.-.-.-.-.-.-.-.-.\                                    ║
        /.-.-.-.-.-.-.-.-.-.\                   .-~~~-.         ║
       /.-.-.-.-.-.-.-.-.-.-.\          .- ~ ~-(       )_ _     ║
      /______/__________\___o_\        /                     ~ -║
      \_______________________/ ◁════════╗     Tailscale        ║ \
                                       \ ║                      ║.'
                ╔ ═ ═ ═ ═ ═ ═ ═ ╗        ║- . _____________ . -~║
                 Made Accessible         ║                      ║
                ║  to Personal  ║        ║                      ║
                   Devices via           ║                      ║
                ║Tailscale Serve║        ║                      ║
                                         ║                      ║
           ╔════╩═══════════════╩════════╝                      ║
           ║                                                    ║
┌──────────╬────────────────────────────────────────────────────╬─────┐
│┌─────────╬────────────────────────────────────────────────────╬────┐│
││┌────────╬────────────────────────────┐    ┌──────────────────╬───┐││
│││ ┌──────╬──────────────────────────┐ │    │ ┌────────────────╬─┐ │││
│││ │      ║     Tailscale            │ │    │ │    Tailscale   ║ │ │││
│││ │      ║                          │ │    │ │                ║ │ │││
│││ └──────╬──────────────────────────┘ │    │ └────────────────╬─┘ │││
│││        ▽                            │    │                  ▽   │││
│││ ┌─────────────┐     ┌─────────────┐ │    │ ┌──────────────────┐ │││
│││ │Immich (Main)│     │   Immich    │ │    │ │     MicroBin     │ │││
│││ │             │◀───▶│ (Database)  │ │    │ │                  │ │││
│││ └─────────────┘     └─────────────┘ │    │ └──────────────────┘ │││
│││        ▲                   ▲        │    │        MicroBin Stack│││
│││        │                   │        │    └──────────────────────┘││
│││        ▼                   ▼        │                ▲           ││
│││ ┌─────────────┐     ┌─────────────┐ │                │           ││
│││ │   Immich    │     │ Immich (ML) │ │                │           ││
│││ │   (Redis)   │◀───▶│             │ │                │           ││
│││ └─────────────┘     └─────────────┘ │                │           ││
│││                         Immich Stack│                │           ││
││└─────────────────────────────────────┘                │           ││
││                   ▲                                   │           ││
││                   │                                   │           ││
││                   └───┬──────────┬───┬────────────────┘           ││
││                        Updates │ │    Updates │                   ││
││                       └ ─ ─ ─ ─  │   └ ─ ─ ─ ─                    ││
││                       ┌─────────────────────┐                     ││
││                       │┌───────────────────┐│                     ││
││                       ││    Watchtower     ││                     ││
││                       ││                   ││                     ││
││                       │└───────────────────┘│                     ││
││                       │     Watchtower Stack│                     ││
││                       └─────────────────────┘                     ││
││Dockge Stack                                                       ││
││Provides an interface to edit, operate, and run all other stacks.  ││
│├───────────────────────────────────────────────────────────────────┤│
││                              Docker                               ││
││             All this stuff runs in docker containers.             ││
│└───────────────────────────────────────────────────────────────────┘│
│                                  ▲                                  │
│                                  ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─  │
│                                  │Automatic updates for docker and│ │
│                                  │     other system utilities       │
│┌────────────────────────┐        │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │
││  unattended-upgrades   │────────┘                                  │
│└────────────────────────┘                   Your Server (Ubuntu LTS)│
└─────────────────────────────────────────────────────────────────────┘

If you follow the entire blog series to its conclusion, the result should be a home server which has the following properties:

What Hardware to Use? #

I am not here to tell you precisely what to use, there are way too many unique constraints and everyone knows what they need better than I could hope to. The following is just a series of things to consider when choosing hardware.

You probably don't need as many resources as you think #

In my personal experience, people who like to make their own servers typically start by making their own gaming PCs. This is excellent, since it means they have a good understanding of the basics. However, they also have a bias towards higher specs. That's not necessary here. Especially if this is your first self-hosted server, you won't need much CPU nor RAM. Save the money, get the cheapest machine you can, probably a MiniPC of some kind.

There are two key exceptions to this that you will want to consider:

  1. If you want to self-host a video-streaming system of some kind (feel free to ignore this segment if you don't), you have to consider how many users you're likely to have at the same time. In order to be streamed, video may need to be transcoded, and this requires a decent CPU/GPU. Useful information about this is included below.
    • You can find information on the transcoding performance of various CPUs here.
    • Video may be streamed directly, however, this depends on the bandwidth between you and the client. If its all on your LAN, then there should be no need to transcode, most LAN speeds are more than fast enough for streaming video, even in 4K. However, streaming outside your network is a bit of a different story. Frankly speaking, especially if this is your first self-hosted server, you should avoid streaming video outside your network, there are too many security considerations.
    • For video, you will also need considerable storage resources (more about that on point #2 below). A 1080p movie is between 2GiB and 50GiB depending on many factors. If we assume that 20GiB is the average (which is approximately my experience), then 1TiB of storage space can hold about 50 movies. You should consider this in your choice is hardware, because most computers don't have much space for disks. The maximum size for a 2.5" disk is 5TiB. While 3.5" disks can get quite a bit bigger, but you would need to make sure that the hardware you buy can support this. Managing large storage arrays is outside the scope of this series of articles.
  2. You need to consider the storage requirements of the apps you want to self-host. Most self-hosted applications require very little space. The docker image takes up more space than the data hosted typically by applications like ByteStash. However, this is not true of media-focused applications, like those that host photos, backup files, and stream video (as described above). Those can be very storage intensive.
    • You should assume that a person needs about 2TiB of space for photos (though consider your knowledge of the specific people involved, some may need more or less than this, the 2TiB is just a basic starting point). This should be more than what they need right now, but you have to remember that phones are getting higher and higher quality cameras, people shoot more and more video, and people take more and more photos.

Unattended Upgrades #

References:

With the basics of the Introduction out of the way, we should get to work configuring our system. The first order of business is unattended-upgrades to keep everything up-to-date.

unattended-upgrades is a pretty standard service for Debian-based Linux Distros, its job is to automatically upgrade all the apt-managed packages1 on your system.

To install unattended-upgrades, simply execute:

1sudo apt update --fix-missing -y;
2sudo apt upgrade -y;
3sudo apt install unattended-upgrades;

Then we need to configure it for automatic upgrades. For that, you have to open the following file with your favorite in-terminal text editor2: /etc/apt/apt.conf.d/50unattended-upgrades

You fill find a pretty complicated configuration, but you only need to change the following lines (many of the lines may need to be uncommented, by removing // in front of the line):

...
"origin=Debian,codename=${{distro_codename}}-updates";
...
"${{distro_id}}:${{distro_codename}}-updates";
...
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
...
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
...
Unattended-Upgrade::MinimalSteps "true";
...
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
...
Unattended-Upgrade::Remove-Unused-Dependencies "true";
...

Just make the corresponding lines in the configuration file look like this (ignoring the lines represented by "..."s, those are just placeholders), and you should be good to go. This will allow your server to automatically upgrade any packages that have updates published for them. However, some package upgrades require the server to reboot, if you want the server to reboot automatically on its own, make sure to set the following lines in the same configuration file (/etc/apt/apt.conf.d/50unattended-upgrades):

Unattended-Upgrade::Automatic-Reboot "true";
...
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
...
Unattended-Upgrade::Automatic-Reboot-Time "{reboot_time}";

Note that you have to set the {reboot_time} to something that works for you. A good example is something like 08:00 which would do the reboot at 8am in whatever timezone the server if configured for.

I personally recommend a reboot time somewhere between 3am and 6am in your timezone since that's when people tend to use their self-hosted servers the least, but you have to decide for yourself.

Other Approaches #

It is worth noting that there are other ways to manage a self-hosted server than the approach outlined in this series of articles. Most notably, there is a series of systems called "Turnkey Operating Systems" which you should take a look at if you're considering self-hosting. The goal is to have a user experience akin to that of an iPhone where you can just go into an app store, click install, and a self-hosted webapp is installed on your server. This sounds pretty great, but there are tradeoffs that you should consider which vary depending on the system in question. Here are some Turnkey operating systems I know of:

What's Next? #

Now that we have a good understanding of what we want to accomplish and have the OS set up, we can jump to setting up networking with Tailscale.



  1. apt is the standard package manager on Debian-based distributions, it is how you install most programs that run directly on the machine. It is how we're going to install docker for example, and we're going to use unattended-upgrades to keep things like docker and its dependencies up to date. ↩︎

  2. If you don't know what an in-terminal text editor is, a pretty good one for beginners is nano which you can use in this case with just: sudo nano /etc/apt/apt.conf.d/50unattended-upgrades ↩︎

last updated: