Docker-based setup for my self-hosted apps/services.
- A home server runs several apps in Docker containers.
- This includes Immich, Jellyfin, Home Assistant, ntfy, etc.
- All apps (with a few exceptions) are auto-updated by Watchtower.
- Services are accessed via Traefik, which also provides TLS and various security features, including:
- The home server has no public IP/ports, so requests to it are tunneled through a small cloud-based proxy server.
- Request proxying is done with FRP.
- This has the additional benefit of hiding the home server's IP as all app domain/subdomain DNS entries point to the cloud proxy.
- The cloud-based proxy also runs a few additional apps itself where appropriate (for example, Syncthing Relay Server).
Note: Diagram may be out of date.
- Services are defined in Docker Compose files. Specifically:
landscape.docker-compose.yaml
: Defines all services that run on the server (except FRPC).frpc.docker-compose.yaml
: Defines the FRPC service (this is separate because starting/stopping FRPC can be much riskier than for other services).landscape-remote.docker-compose.yaml
: Defines all services that run on the remote proxy.
- In order to have multiple instances of this setup (staging, production, etc.) and protect secret values from being checked in to Git, all environment-specific configuration and/or sensitive information is stored in environment-specific Git-ignored variable files.
template.VARS.sh
: Example file used as a starting point for a user to define their ownVARS.sh
containing environment-specific variables and secrets.- For the install script to run, at least one of the following user-defined files must exist (listed in order of preference):
VARS.production.sh
VARS.staging.sh
VARS.sh
- Setup scripts:
install.sh
andinstall_remote.sh
: Scripts used to install and start services on the server and remote proxy respectively.simple_restart.sh
: Restart a running service.update_frp.sh
: Check for FRPC/FRPS updates and install them if needed (auto-updating these is too risky).
- Other files:
fixed.VARS.sh
: Hardcoded variables used by various scripts.prep_env.sh
: Helper script used by various other scripts.- Everything in
files/
: Various files used to configure/initialize apps and/or used by the install scripts. - Everything in
mock_data/
: Data used to demo some services in a staging environment.- Some services depend on a specific directory hierarchy on the server. For example, Immich looks for images in
Main/Media/Camera/
. - The
mock_data
folder reflects this hierarchy. - In production, the root of this hierarchy would be the user's home directory (instead of
mock-data/
).
- Some services depend on a specific directory hierarchy on the server. For example, Immich looks for images in
- Set up a server with outbound internet access.
- Most of the code is distro-agnostic but a few lines are not. We assume the server is running Fedora 41.
- The server must be powerful enough to run all apps defined in
landscape.docker-compose.yaml
. - If the server has a LUKS-encrypted drive (optional), a way to decrypt it remotely via SSH will be installed as part of the setup process.
- Clone this repo on the server and create a copy of
template.VARS.sh
namedVARS.sh
(orstaging.VARS.sh
orproduction.VARS.sh
). Fill in the values as appropriate. You may benefit from reading through the rest of these steps first. - Modify any of the source files in a fork of this repo, as appropriate to fit your needs.
- This is optional since all user/environment-specific information comes from the user-defined
VARS.sh
file. - But this repo is tailored to the author's needs - it is likely that you will at least want to disable certain apps or change some hardcoded app settings.
- This is optional since all user/environment-specific information comes from the user-defined
- Ensure the server contains the following directories as defined in your
VARS.sh
file:MAIN_PARENT_DIR
: Your data, used by the apps.- This is typically just your home directory.
- All apps that work with your personal data (photos, notes, etc.) expect that data to be stored in a specific folder structure inside this directory. You must create that structure and arrange your files accordingly - use
mock-data/
as a template to copy from.
STATE_DIR
: Persistent internal storage/state for all apps.- Set to
./state/
by default. - Running services exclusively rely on this folder and/or named Docker volumes to store their internal data.
- The folder and everything in it is auto-generated and should not be modified.
- Significance of the data varies on a per-app basis - some apps store ephemeral state (for example generated video thumbnails) while others store more important long-term data that should be backed up (for example Home Assistant settings).
- Set to
- Set up a proxy server with outbound and inbound internet access (public IP/ports).
- Ensure it is reachable via SSH from your main server.
- We assume this is running Ubuntu 24.04.
- Purchase a domain for your apps, and set up DNS rules for each app subdomain, all pointing to the remote proxy server's IP.
- For a list of all required subdomains, run:
source prep_env.sh; findDomainsInSetup
- For a list of all required subdomains, run:
- Run
install_remote.sh
on the main server to remotely setup everything needed on the proxy server. - Run
install.sh
on the main server to install all apps/services on the main server. - Some apps require manual initialization after they have been installed.
- It is dangerous to publicly expose these apps without initializing them, since they may allow for unauthorized access.
- For this reason, certain apps are temporarily protected with Authelia authentication middleware upon initial install, even when those apps would not usually be protected in their final post-initialization state (due to having their own authentication, or having specific client needs that are not compatible with Authelia).
- At this stage, you must manually complete the setup process for each of these apps. For a list of these apps' domains, run the following command:
source prep_env.sh; cat files/authelia.config.yaml | envsubst | grep -Eo 'domain:.+# IGNORE INITIALLY' | awk '{print $2}'
- Once finished, re-run
install.sh
. This time, the Authelia middleware will not apply to those apps.
- Setup is complete.
- Remember to occasionally run
update_frp.sh
to keep FRP up to date.
- Remember to occasionally run