❖ Introduction

Tilde aims to be a starting point for anyone getting started in their self-hosting journey. It uses Pyinfra to configure a new server and deploy commonly used applications using Docker. Please note that Tilde only targets and supports Debian.

A diagram showing services running in a homeserver

❖ Features

Tilde configures the following things for you:

  • A Docker install to easily spin up other services
  • A Wireguard tunnel with a user-friendly interface so you can access your services when you're not home.
  • Dynamic DNS using DuckDNS so that Wireguard can always connect to your server
  • A Nextcloud instance for cloud storage
  • A Jellyfin instance for media consumption
  • Grafana with Prometheus for server monitoring
    • Comes with Node Exporter (System Stats) and cadvisor (Container Stats) pre-configured. Add any dashboard compatible with them.
  • A Nginx Proxy Manager Instance for reverse proxy and TLS (Configured by the user)

❖ Requirements

Tilde is meant to be very hands off, but there are still a few things you'll need to do manually to set it up for success.

It's not very complicated, but the instructions are written under the assumption that you're at least slightly comfortable with the unix commandline.

We'll go over configuring the requirements for the following things in this section:

  1. Server configuration and SSH
  2. Port Forwarding
  3. Dynamic DNS
  4. Environment Variables

❖ Server Configuration and SSH

❖ Operating System

The target server should be running Debian 12 or above. The Install Images provided by Debian include a Graphical Installer, and a TUI installer as well. Feel free to use whichever one you are more comfortable with.

❖ SSH

If you don't have ssh keys set up, please take a look at the Github Docs on the topic.

Your ssh config should should look something like this:

# ~/.ssh/config

host <homeserver>
  HostName <internal-ip-of-server> 
  user <non-root-user>

host *
  AddKeysToAgent yes
  IdentityFile ~/.ssh/id_ed25519

note

Replace the values wrapped in <> with the appropriate values for your server

The password authentication can be skipped by adding your public key to your server. You can do so manually by placing it in the ~/.ssh/authorized_keys file on your server. Or you can use the following command to do it for you:

ssh-copy-id <homeserver>

❖ Sudo User

For the sake of security and to generally make our lives easier, we will not be logging into the server as root. The Debian Installer should have guided you to create another user. But just in case it didn't, if you're already logged in as root, you can create a new user by running the following command:

adduser <username>

Once we have another user, we need to give that user sudo privileges. On Debian, sudo is not installed by default. We will run the following commands to install sudo and give our user the proper permissions.

apt install sudo                  # Install sudo
usermod -aG sudo <username>       # Add sudo permissions for our user

❖ Laptop Configuration (Optional)

If you are using an old laptop as your server, we want to make sure that the laptop does not go to sleep when the lid is closed. We can do so by following the steps below.

Open /etc/systemd/logind.conf in your preferred text editor

sudo vim /etc/systemd/logind.conf

Uncomment and set the HandleLidSwitch option to ignore

HandleLidSwitch=ignore

Restart the systemd daemon:

sudo systemctl restart systemd-logind

warning

This will log you out

Taken from ask Ubuntu

❖ Port Forwarding

When accessing our services away from home, we'll be using a Wireguard tunnel to VPN into our home network. For this to work however, we need to set up Port Forwarding.

For some background, Tilde configures Wireguard to run on the default port 51820. Let's assume that your Server's internal IP is 192.168.1.30. We need to tell our Router to send any outside traffic on port 51820 to 192.168.1.30:51820.

Setting up port forwarding is different from one Router to another. Please look up how to do so for your Router.

Here are some links to point you in the right direction:

❖ Dynamic DNS

❖ Overview

Unless you pay for a static IP, your ISP is free to change your public IP whenever they want. This is usually not a problem, but it is for us. We need to make sure that Wireguard can find our home's public IP at all times.

This is where Dynamic DNS will come to our rescue.

Tilde uses ZenDNS to periodically update the DNS records. While this guide assumes that DuckDNS will be used, ZenDNS also works with Cloudflare and Namecheap. Please read the docs for ZenDNS to configure what works best for you.

It's worth mentioning that I am the author of ZenDNS as well. I wrote it because updating DNS entries through API calls is actually pretty easy. Other tools always seemed more complicated than they had to be. With that said, that's just my experience. If ZenDNS doesn't work well for you please feel free to switch to a Dynamic DNS client like inadyn or ddclient. You can even write your own!

❖ Example

Assuming you're using DuckDNS:

  • Sign up for an account
  • Pick a new subdomain on the website and click add domain
  • Copy the token from the website as well

Fill out src/tilde/templates/zendns.yaml.j2. It should look something like:

duckdns:
  - token: "your-token"
    domain: "your-subdomain.duckdns.org"

If you're not using DuckDNS, look at the docs for ZenDNS and replace the contents of src/tilde/templates/zendns.yaml.j2 with configuration for your chosen provider.

❖ Environment Variables

As the final pre-requisite before we deploy, you'll also need to fill out some environment variables in the run script.

# run.sh

export USERNAME=<username>
export HOST=<server-from-your-ssh-config>

export WIREGUARD_PASSWORD=<password-you-want-for-wireguard-ui>
export WIREGUARD_HOST=<your-subdomain.duckdns.org>

# Can be obtained by running `id $user`
export TILDE_UID=<uid-here>
export TILDE_GID=<gid-here>

export TZ=America/New_York

note

See TZ Database to set your TimeZone properly.

Also don't forget to get rid of the <>

❖ Deployment

After all that work, we are finally ready to deploy!!

Tilde only has one dependency: pyinfra. You can either use uv like I do, or just use a venv and install pyinfra yourself, it's totally up to you!

❖ Using uv

uv sync                         # Create a virutal environment and install pyinfra
source .venv/bin/activate       # Activate the virutal environment
./run.sh                        # Run tilde on your homeserver

❖ Using a venv

python3 -m venv tilde_venv          # Create a venv for tilde
source tilde_venv/bin/activate      # Activate the venv
pip3 install pyinfra                # Install pyinfra and dotenv
./run.sh                            # Run tilde on your homeserver

If everything goes as expected, you'll have a shiny new home server complete with all the features mentioned earlier!

The services are available at homeserver-internal-ip:service-port

The mapping for the ports is as follows:

Nginx Proxy Manager:       81
Wireguard:                 51821
Grafana:                   3000
Jellyfin:                  8096
Nextcloud:                 8080

❖ Service Configuration

You have your shiny new server! Let's talk a little about the services running on this server. There is not much to say about most of these, just things to keep in mind and some tips!

The services running are as follows:

note

There are other services running, but they are auxiliary to the ones listed above. Run docker ps to see them.

❖ Nginx Proxy Manager

Nginx Proxy Manager is a fancy frontend for Nginx. I'll be calling it NPM from this point forward, and you can expect to see this abbreviation pop up in self hosting communities as well.

If you own a domain, you can use NPM to set up a reverse proxy for your services. This way, you can access jellyfin at jellyfin.your-domain.com instead of 192.168.1.30:8096.

The first time login credentials for NPM are as follows:

Email:    admin@example.com
Password: changeme

You can also use NPM to set up TLS/SSL for your services. This will make your browser stop complaining about an insecure connection and let you access your services over HTTPS.

I won't go over how to do that here, but I will link this incredibly helpful Video by Wolfgang's Channel on the topic.

❖ Wireguard

Tilde uses wg-easy to make Wireguard VPN access easy. The password for the UI will be the one set in the run script. You can add profiles for all of your devices, and access your services from anywhere.

Do note that IP collisions can happen when using Wireguard. This is when your current network's private address space is the same as your home network.

For example, devices in your home network are assigned IP address 192.168.1.0 -> 192.168.1.255 and the same is true of the network you are currently connected to. If you're at a friend's house, you can ask them to change their private address space to be something like 192.168.2.0 -> 192.168.2.255. You can also change yours to accomplish the same thing.

This is an oversimplification of how private address spaces are assigned, but illustrates a common problem that I've run into multiple times.

❖ Grafana

Tilde configures Grafana with Prometheus for server monitoring.

In short, Prometheus scrapes a set of data sources and serves that data through Prometheus Query Language. Grafana then takes that data, and displays them to you in a beautiful UI.

The following Prometheus data sources come pre-configured with Tilde:

You can then use pre-made Grafana Dashboards to visualize that data. For example, I like using Node Exporter Full as one of my dashboards. This relies on Node Exporter being set up for Prometheus.

❖ Jellyfin

Not a ton to say about Jellyfin. Jellyfin will act as the central hub for most of your media. Just remember to set up the libraries with the proper path during setup.

TV:         /data/tv
Movies:     /data/movies
Music:      /data/music

Take a look at the Jellyfin Docker Compose Template to understand why that is. And look at the Jellyfin Pyinfra Task to understand where you should put your media for Jellyfin to pick up.

❖ Nextcloud

Tilde sets up a very basic Nextcloud instance. This is not the most performant or feature-complete version of Nextcloud, but it is the easiest for a beginner.

The rabbit hole for Nextcloud goes very deep, take a look at the following to learn more: