├── homelab ├── builds │ ├── nextcloud-app │ │ └── Dockerfile │ └── nginx-proxy │ │ ├── uploadsize.conf │ │ └── Dockerfile ├── env │ ├── nextcloud-db.env │ └── gitea-db.env ├── README.org ├── maintenance.org ├── docker-compose.yml └── setup.org ├── gameservers ├── terraria │ ├── setup.org │ ├── maintenance.org │ ├── README.org │ └── docker-compose.yml ├── minecraft │ ├── setup.org │ ├── maintenance.org │ ├── docker-compose.yml │ └── README.org ├── modded-minecraft │ ├── setup.org │ ├── maintenance.org │ ├── docker-compose.yml │ └── README.org └── README.org ├── img └── self-hosted-dashboard.png ├── network ├── maintenance.org ├── README.org ├── docker-compose.yml ├── wireguard-setup.org └── pi-hole-setup.org ├── LICENSE ├── README.org └── setup.org /homelab/builds/nextcloud-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nextcloud:apache 2 | -------------------------------------------------------------------------------- /gameservers/terraria/setup.org: -------------------------------------------------------------------------------- 1 | #+title: Terraria Server Setup 2 | #+author: Emmet 3 | -------------------------------------------------------------------------------- /gameservers/minecraft/setup.org: -------------------------------------------------------------------------------- 1 | #+title: Vanilla Minecraft Server Setup 2 | #+author: Emmet 3 | -------------------------------------------------------------------------------- /gameservers/terraria/maintenance.org: -------------------------------------------------------------------------------- 1 | #+title: Terraria Server Maintenance 2 | #+author: Emmet 3 | -------------------------------------------------------------------------------- /gameservers/modded-minecraft/setup.org: -------------------------------------------------------------------------------- 1 | #+title: Modded Minecraft Server Setup 2 | #+author: Emmet 3 | -------------------------------------------------------------------------------- /gameservers/terraria/README.org: -------------------------------------------------------------------------------- 1 | #+title: Terraria Server with Docker Compose 2 | #+author: Emmet 3 | -------------------------------------------------------------------------------- /gameservers/minecraft/maintenance.org: -------------------------------------------------------------------------------- 1 | #+title: Vanilla Minecraft Server Maintenance 2 | #+author: Emmet 3 | -------------------------------------------------------------------------------- /homelab/builds/nginx-proxy/uploadsize.conf: -------------------------------------------------------------------------------- 1 | client_max_body_size 10G; 2 | proxy_request_buffering off; 3 | -------------------------------------------------------------------------------- /gameservers/modded-minecraft/maintenance.org: -------------------------------------------------------------------------------- 1 | #+title: Modded Minecraft Server Maintenance 2 | #+author: Emmet 3 | -------------------------------------------------------------------------------- /img/self-hosted-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/librephoenix/selfhosted-templates/HEAD/img/self-hosted-dashboard.png -------------------------------------------------------------------------------- /homelab/builds/nginx-proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginxproxy/nginx-proxy:alpine 2 | 3 | COPY uploadsize.conf /etc/nginx/conf.d/uploadsize.conf 4 | 5 | -------------------------------------------------------------------------------- /homelab/env/nextcloud-db.env: -------------------------------------------------------------------------------- 1 | # MYSQL ROOT PASSWORD 2 | MYSQL_ROOT_PASSWORD=CHANGEME 3 | 4 | # NEXTCLOUD DB NAME 5 | MYSQL_DATABASE=nextcloud 6 | 7 | # NEXTCLOUD DB USER 8 | MYSQL_USER=nextcloud 9 | 10 | # NEXTCLOUD DB PASSWORD 11 | MYSQL_PASSWORD=CHANGEME 12 | -------------------------------------------------------------------------------- /homelab/env/gitea-db.env: -------------------------------------------------------------------------------- 1 | # MYSQL ROOT PASSWORD 2 | MYSQL_ROOT_PASSWORD=CHANGEME 3 | 4 | # GITEA DB NAME, THESE SHOULD BE THE SAME 5 | GITEA__database__NAME=gitea 6 | MYSQL_DATABASE=gitea 7 | 8 | # GITEA DB USER, THESE SHOULD BE THE SAME 9 | GITEA__database__USER=gitea 10 | MYSQL_USER=gitea 11 | 12 | # GITEA DB PASSWORD, THESE SHOULD BE THE SAME 13 | GITEA__database__PASSWD=CHANGEME 14 | MYSQL_PASSWORD=CHANGEME 15 | -------------------------------------------------------------------------------- /gameservers/README.org: -------------------------------------------------------------------------------- 1 | #+title: Gameservers Docker Compose 2 | #+author: Emmet 3 | 4 | ** Service Overview 5 | Within this directory, there are several templates for gameservers: 6 | - [[./minecraft][Vanilla Minecraft]] 7 | - [[./modded-minecraft][Modded Minecraft]] 8 | - [[./terraria][Terraria]] 9 | 10 | ** Setup 11 | For details on how to turn these into working game servers, see setup.org in the relevant subdirectory. 12 | 13 | ** Maintenance 14 | For more details on maintaining these game servers, see maintenance.org in the relevant subdirectory. 15 | -------------------------------------------------------------------------------- /gameservers/terraria/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | terraria-server: 5 | image: ghcr.io/beardedio/terraria:latest 6 | container_name: terraria-server 7 | stdin_open: true 8 | tty: true 9 | environment: 10 | - world=YourWorldName # set this to your world's filename (without extension) if you already have a world save in the config directory 11 | # comment out the above two lines on first start if you don't have a world and need to generate one 12 | networks: 13 | - proxy-tier 14 | volumes: 15 | - ./config:/config 16 | ports: 17 | - 7777:7777 18 | restart: unless-stopped 19 | 20 | networks: 21 | proxy-tier: 22 | external: true 23 | -------------------------------------------------------------------------------- /gameservers/minecraft/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | vanilla-minecraft-server: 5 | image: itzg/minecraft-server:java8-multiarch 6 | container_name: vanilla-minecraft-server 7 | stdin_open: true 8 | tty: true 9 | environment: 10 | - MEMORY=2G # set this based on number of players you expect 11 | - EULA=TRUE 12 | - VERSION=1.20 # it's a good idea to set this statically, and only update minor versions 13 | # updating to a new major version on the same world will make borders between new and old chunks clash 14 | - TYPE=VANILLA 15 | networks: 16 | - proxy-tier 17 | volumes: 18 | - ./data:/data 19 | ports: 20 | - 25565:25565/udp 21 | - 25565:25565/tcp 22 | restart: unless-stopped 23 | 24 | networks: 25 | proxy-tier: 26 | external: true 27 | -------------------------------------------------------------------------------- /gameservers/modded-minecraft/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | modded-minecraft-server: 5 | image: itzg/minecraft-server:latest 6 | container_name: modded-minecraft-server 7 | stdin_open: true 8 | tty: true 9 | environment: 10 | - MEMORY=4G 11 | - EULA=TRUE 12 | - VERSION=1.20 # it's a good idea to set this statically, and only update minor versions 13 | # updating to a new major version on the same world will make borders between new and old chunks clash 14 | - TYPE=FABRIC 15 | # - TYPE=FORGE 16 | # before you start the container, make sure your mods are in the data/mods directory! 17 | # some mods, especially biome mods, won't work right unless they are there when the world is generated 18 | networks: 19 | - proxy-tier 20 | volumes: 21 | - ./data:/data 22 | ports: 23 | - 27565:25565/udp 24 | - 27565:25565/tcp 25 | restart: unless-stopped 26 | 27 | networks: 28 | proxy-tier: 29 | external: true 30 | -------------------------------------------------------------------------------- /gameservers/minecraft/README.org: -------------------------------------------------------------------------------- 1 | #+title: Vanilla Minecraft Server with Docker Compose 2 | #+author: Emmet 3 | 4 | This directory contains: 5 | - [[./docker-compose.yml][docker-compose.yml]] - This stores information about the Minecraft server container that is needed, and where server data should be stored 6 | - data - This stores the Minecraft server data and is generated when first starting the server 7 | 8 | ** Service Overview 9 | Within the [[./docker-compose.yml][docker-compose file]], some basic configuration of the Minecraft server can be instanced, namely: 10 | - Minecraft Version 11 | - Minecraft Server Port (if you want something different than the default 25565) 12 | - Memory Limit (RAM Usage) for the Minecraft Server 13 | 14 | Other server configuration can be edited directly in the data directory later. 15 | 16 | ** Setup 17 | For details on how to turn this into a working gameserver, see [[./setup.org][setup.org]]. 18 | 19 | ** Maintenance 20 | For more details on maintaining this gameserver, see [[./maintenance.org][maintenance.org]]. 21 | -------------------------------------------------------------------------------- /network/maintenance.org: -------------------------------------------------------------------------------- 1 | #+title: Network Customization Maintenance 2 | #+author: Emmet 3 | 4 | Maintenance of the Pi-hole and Wireguard services is pretty simple. 5 | 6 | ** Backup 7 | Unless you extensively configure them, they don't take that long to setup and don't require much persistent storage, so you'd probably be ok not backing up the data folders associated with them. 8 | 9 | ** Updates 10 | Regular updates are very important, especially for Wireguard. Updates can be applied by stopping the containers, pulling new images and starting the containers up again: 11 | #+BEGIN_SRC sh :noexec 12 | sudo docker-compose stop 13 | sudo docker-compose pull 14 | sudo docker-compose up -d 15 | #+BEGIN_SRC 16 | 17 | Old Docker images from previous updates can be pruned after verifying the updated containers are working as expected: 18 | #+BEGIN_SRC sh :noexec 19 | sudo docker image prune 20 | #+END_SRC 21 | 22 | *** Pi-hole Blocklist Updates 23 | Blocklists may not be automatically updated. To update your blocklists, navigate to http://yourlocalip:8093/admin/gravity.php and click "Update" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Emmet K 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+title: Self-hosted Docker Compose Setup Templates 2 | #+author: Emmet 3 | 4 | This is a repo I'm using to stash templated information about my self-hosted homelab. 5 | 6 | [[img/self-hosted-dashboard.png]] 7 | 8 | Inside here are templates I've crafted through experimenting with self-hosting many different services. 9 | 10 | I have 3 templates currently: 11 | - [[./homelab][homelab]] - These included self-hosted cloud services such as [[https://nextcloud.com/][Nextcloud]], [[https://syncthing.net/][Syncthing]], [[https://about.gitea.com/][Gitea]], [[https://freshrss.org/][FreshRSS]], and a personal dashboard ([[https://heimdall.site/][Heimdall]]) 12 | - [[./network][network]] - These include network goodies such as [[https://pi-hole.net/][Pi-hole]] (network-level adblock) and [[https://www.wireguard.com/][Wiregaurd]] (to VPN into my home network) 13 | - [[./gameservers][gameservers]] - What it sounds like, self-hosted servers for games like [[https://www.minecraft.net/en-us][Minecraft]], [[https://terraria.org/][Terraria]], etc... 14 | 15 | ** Server Setup 16 | For details on how I prepare my Linux servers, see [[./setup.org][setup.org]]. 17 | 18 | ** Docker-compose (Services) Setup 19 | For details on how I configure docker-compose for my various self-hosted services, visit the respective directory: 20 | - [[./homelab][homelab]] 21 | - [[./network][network]] 22 | - [[./gameservers][gameservers]] 23 | -------------------------------------------------------------------------------- /network/README.org: -------------------------------------------------------------------------------- 1 | #+title: Network Docker Compose 2 | #+author: Emmet 3 | 4 | This directory contains: 5 | - [[./docker-compose.yml][docker-compose.yml]] - This stores information about what containers are needed, how those containers are connected, and where data should be stored 6 | - data - Once all of the docker containers referenced in the compose file are started, persistent storage is created here 7 | 8 | ** Service Overview 9 | Within the [[./docker-compose.yml][docker-compose file]], just two services are started: 10 | - [[https://www.wireguard.com/][Wiregaurd]] - So I can VPN into my house if necessary 11 | - [[https://pi-hole.net/][Pi-hole]] - Network-level adblock and custom DNS! 12 | 13 | ** Pi-hole Setup 14 | [[https://pi-hole.net/][Pi-hole]] is a network-level adblock and custom DNS server for your network! The adblocking capabilities can save you a lot of bandwidth, and it can even be configured to allow safe browsing for your kids (block gambling, chat, social and porn sites). For details on how to turn this into a working production environment, see [[./pi-hole-setup.org][pi-hole-setup.org]]. 15 | 16 | ** Wireguard Setup 17 | [[https://www.wireguard.com/][Wiregaurd]] is a modern, lightweight and secure VPN protocol. If setting this up on a home network, you can use this to VPN into your home network to control local devices while away. For details on how to set this up, see [[./wireguard-setup.org][wireguard-setup.org]]. 18 | 19 | ** Maintenance 20 | For more details on maintaining this environment, see [[./maintenance.org][maintenance.org]]. 21 | -------------------------------------------------------------------------------- /gameservers/modded-minecraft/README.org: -------------------------------------------------------------------------------- 1 | #+title: Modded Minecraft Server with Docker Compose 2 | #+author: Emmet 3 | 4 | This directory contains: 5 | - [[./docker-compose.yml][docker-compose.yml]] - This stores information about the Minecraft server container that is needed, and where server data should be stored 6 | - data - This stores the Minecraft server data and is generated when first starting the server 7 | - data/mods - This is where mods should be placed (if you're using [[https://files.minecraftforge.net/net/minecraftforge/forge/][Forge]] or [[https://fabricmc.net/][Fabric]], which seem to be two most popular). Certain mods (especially custom biome mods) /must/ be here /before/ the containers are first started and the world is generated. Adding these mods later can mess with the world generation if the world was generated without them. 8 | 9 | ** Service Overview 10 | Within the [[./docker-compose.yml][docker-compose file]], some basic configuration of the Minecraft server can be instanced, namely: 11 | - Minecraft Version 12 | - Minecraft Modloader Type (i.e. [[https://files.minecraftforge.net/net/minecraftforge/forge/][Forge]], [[https://fabricmc.net/][Fabric]], etc... full list of available types are listed [[https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/][here]]) 13 | - Minecraft Server Port (if you want something different than the default 25565) 14 | - Memory Limit (RAM Usage) for the Minecraft Server 15 | 16 | Other server configuration can be edited directly in the data directory later. 17 | 18 | ** Setup 19 | For details on how to turn this into a working gameserver, see [[./setup.org][setup.org]]. 20 | 21 | ** Maintenance 22 | For more details on maintaining this gameserver, see [[./maintenance.org][maintenance.org]]. 23 | -------------------------------------------------------------------------------- /homelab/README.org: -------------------------------------------------------------------------------- 1 | #+title: Homelab Docker Compose 2 | #+author: Emmet 3 | 4 | This directory contains: 5 | - [[./docker-compose.yml][docker-compose.yml]] - This stores information about what containers are needed, how those containers are connected, and where data should be stored 6 | - [[./builds][builds]] - These are custom Dockerfiles that are utilized in the compose file 7 | - [[./env][env]] - These contain environment files (used to set passwords and other sensitive information, I have redacted these) 8 | - data - Once all of the docker containers referenced in the compose file are started, persistent storage is created here 9 | 10 | ** Service Overview 11 | Within the [[./docker-compose.yml][docker-compose file]], several services are started, namely: 12 | - [[https://nextcloud.com/][Nextcloud]] (Cloud Storage + Collaboration Suite) 13 | - [[https://syncthing.net/][Syncthing]] (P2P File Synchronization) 14 | - [[https://about.gitea.com/][Gitea]] (Git Server) 15 | - [[https://freshrss.org/][FreshRSS]] (RSS Aggregator) 16 | - [[https://heimdall.site/][Heimdall]] (Personal Dashboard) 17 | 18 | Access to these services is mediated with HTTPS via Nginx Proxy and the Let's Encrypt Companion. With these, it is trivial to add new services; all you need to add a new service is declare these environment variables in the target container: 19 | - =VIRTUAL_HOST= - set this to domain name (sub.domain.example.com) 20 | - =LETSENCRYPT_HOST= - also set this to domain name (sub.domain.example.com) 21 | - =LETSENCRYPT_EMAIL= - email to be used in Let's Encrypt SSL request; *this is kept on record by Let's Encrypt* 22 | 23 | ** Setup 24 | For details on how to turn this into a working production environment, see [[./setup.org][setup.org]]. 25 | 26 | ** Maintenance 27 | For more details on maintaining this environment, see [[./maintenance.org][maintenance.org]]. 28 | -------------------------------------------------------------------------------- /network/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | wireguard-vpn: 5 | image: lscr.io/linuxserver/wireguard:latest 6 | container_name: wireguard-vpn 7 | cap_add: 8 | - NET_ADMIN 9 | - SYS_MODULE #optional 10 | environment: 11 | - PUID=1000 12 | - PGID=1000 13 | - TZ=YourRegion/YourCity 14 | - SERVERURL=yourdomain.com 15 | - SERVERPORT=51820 16 | - PEERS=1 #optional 17 | - PEERDNS=auto #optional 18 | # - INTERNAL_SUBNET=10.13.13.0 #optional 19 | # - ALLOWEDIPS=0.0.0.0/0 #optional 20 | # - PERSISTENTKEEPALIVE_PEERS= #optional 21 | # - LOG_CONFS=true #optional 22 | networks: 23 | - proxy-tier 24 | volumes: 25 | - ./data/wireguard-vpn/config:/config 26 | # - /lib/modules:/lib/modules #optional 27 | ports: 28 | - 51820:51820/udp 29 | sysctls: 30 | - net.ipv4.conf.all.src_valid_mark=1 31 | restart: unless-stopped 32 | 33 | pihole-adblock: 34 | container_name: pihole-adblock 35 | image: pihole/pihole:latest 36 | # For DHCP it is recommended to remove these ports and instead add: network_mode: "host" 37 | networks: 38 | - proxy-tier 39 | ports: 40 | - "53:53/tcp" 41 | - "53:53/udp" 42 | # - "67:67/udp" # Only required if you are using Pi-hole as your DHCP server 43 | - "8093:80/tcp" 44 | - "8094:443/tcp" 45 | environment: 46 | TZ: 'YourRegion/YourCity' 47 | WEBPASSWORD: CHANGEME 48 | volumes: 49 | - './data/pihole-adblock/etc/pihole:/etc/pihole' 50 | - './data/pihole-adblock/etc/dnsmasq.d:/etc/dnsmasq.d' 51 | # https://github.com/pi-hole/docker-pi-hole#note-on-capabilities 52 | # cap_add: 53 | # - NET_ADMIN # Required if you are using Pi-hole as your DHCP server, else not needed 54 | restart: unless-stopped 55 | 56 | networks: 57 | proxy-tier: 58 | external: true 59 | -------------------------------------------------------------------------------- /network/wireguard-setup.org: -------------------------------------------------------------------------------- 1 | #+title: Wireguard Setup 2 | #+author: Emmet 3 | 4 | ** Docker Networking 5 | By default, only docker-compose containers within the same file are able to talk to each other. This can be bypassed by creating an "external" network. In this sample, the external network is called "proxy-tier" and must be created manually with the command: 6 | #+BEGIN_SRC sh :noexec 7 | sudo docker network create proxy-tier 8 | #+END_SRC 9 | 10 | ** Firewall Rules 11 | In order to make sure everything works, the both the server's firewall and your router's firewall must be configured to allow access to the ports we need. Port 52180 is the only port needed for Wireguard. 12 | 13 | - Allow Incoming Port 51820 14 | 15 | In my config ([[https://gitlab.com/librephoenix/nixos-config][GitLab]], [[https://github.com/librephoenix/nixos-config][GitHub]]), all that needs to be set are the rules inside of firewall.nix: 16 | #+BEGIN_SRC nix 17 | { config, pkgs, ... }: 18 | 19 | { 20 | # Firewall 21 | networking.firewall.enable = true; 22 | # Open ports in the firewall. 23 | networking.firewall.allowedTCPPorts = [ 51820 ]; 24 | networking.firewall.allowedUDPPorts = [ 51820 ]; 25 | } 26 | #+END_SRC 27 | 28 | ** Configuration 29 | Set the =PEERS= environment variable to the amount of peers you want to to be able to connect to the Wireguard server. Set the =TZ= environment variable to your timezone and the =SERVERURL= to a domain name or public IP pointing to the server. 30 | 31 | ** Start Container(s) 32 | Once everything is properly configured and you've double-checked it, you can start all the necessary containers by running: 33 | #+BEGIN_SRC sh :noexec 34 | # inside of homelab directory 35 | sudo docker-compose up -d 36 | #+END_SRC 37 | 38 | ** Copy Config to Clients (Peers) 39 | In Wireguard, the clients are called "peers." Once the Wireguard container has been started, it will automatically create as many peer (client) configurations as you specified in the =PEERS= variable. These configurations with be in =data/wireguard-vpn/config/peer[n]=. Simply copy these configurations to client devices and import the configurations into the Wireguard client on your device. 40 | -------------------------------------------------------------------------------- /homelab/maintenance.org: -------------------------------------------------------------------------------- 1 | #+title: Homelab Maintenance 2 | #+author: Emmet 3 | 4 | ** Backup 5 | Since all important (persistent) data will be stored inside this directory (uploaded files are in the data directory), backing up that data to multiple locations is very important. 6 | 7 | To be more specific, the rule goes something like: "If data does not exist in at least 3 physical drives, and at least 2 geographically separated locations, then it does not exist." It is a good idea to set up: 8 | - A local backup on a separate SSD or flash drive 9 | - This protects against your main hard drive failing 10 | - Additionally, if it is disconnected, this protects against ransomware and/or other hacks where access to data is compromised by an attacker 11 | - A remote backup in the cloud (or someone else's computer) 12 | - This protects you against a home disaster such as fire, flooding, theft 13 | 14 | *** Local Backup 15 | A local backup is quickly and easily achieved with a tool like =rsync=, which incrementally transfers data (only transfers changes). This means that the first backup might take a few hours or days, but subsequent backups can take a mere seconds or minutes. For file versioning, you can go a step further and use something like =rdiff-backup=, which creates backup versions, but leverages =rsync= in the background for performance. 16 | 17 | For privacy and security, backups should be encrypted. For this, you can either backup the encrypted =crypt= directory to a flash drive or SSD, or backup the decrypted =plain= directory to an SSD or flash drive with =block encryption=. The =block encryption= method has the added benefit of masking file directory structures and relative file sizes. 18 | 19 | *** Remote Backup 20 | An ideal remote backup would involve directly controlling the remote computer via SFTP and backing up with rdiff-backup. However, most cloud storage solutions don't allow you to do this. As a viable alternative, remote backups can easily be achieved with =rclone=. 21 | 22 | To setup rclone for a particular cloud provider run 23 | #+BEGIN_SRC sh :noexec 24 | sudo rclone config 25 | #+END_SRC 26 | 27 | Then, to backup the encrypted gocryptfs storage: 28 | #+BEGIN_SRC sh :noexec 29 | sudo rclone sync --exclude=gocryptfs.conf crypt yourbackup:/backup 30 | #+END_SRC 31 | 32 | ** Updates 33 | Regular updates are very important, especially for publicly accessible apps. Updates can be applied by stopping the containers, pulling new images and starting the containers up again: 34 | #+BEGIN_SRC sh :noexec 35 | sudo docker-compose stop 36 | sudo docker-compose pull 37 | sudo docker-compose up -d 38 | #+BEGIN_SRC 39 | 40 | Old Docker images from previous updates can be pruned after verifying the updated containers are working as expected: 41 | #+BEGIN_SRC sh :noexec 42 | sudo docker image prune 43 | #+END_SRC 44 | -------------------------------------------------------------------------------- /network/pi-hole-setup.org: -------------------------------------------------------------------------------- 1 | #+title: Pi-hole Setup 2 | #+author: Emmet 3 | 4 | This walks you through setup of Pi-hole on your home network, which can block ads (or any domain for that matter) /and/ act as a local DNS server you can control. 5 | 6 | ** Docker Networking 7 | By default, only docker-compose containers within the same file are able to talk to each other. This can be bypassed by creating an "external" network. In this sample, the external network is called "proxy-tier" and must be created manually with the command: 8 | #+BEGIN_SRC sh :noexec 9 | sudo docker network create proxy-tier 10 | #+END_SRC 11 | 12 | ** Firewall Rules 13 | In order to make sure everything works, the both the server's firewall and your router's firewall must be configured to allow access to the ports we need. Port 8093 will be the Web GUI for administration, and port 53 since that is the standard port for DNS servers. 14 | 15 | - Allow Incoming Port 8093 16 | - Allow Incoming Port 53 17 | 18 | In my config ([[https://gitlab.com/librephoenix/nixos-config][GitLab]], [[https://github.com/librephoenix/nixos-config][GitHub]]), all that needs to be set are the rules inside of firewall.nix: 19 | #+BEGIN_SRC nix 20 | { config, pkgs, ... }: 21 | 22 | { 23 | # Firewall 24 | networking.firewall.enable = true; 25 | # Open ports in the firewall. 26 | networking.firewall.allowedTCPPorts = [ 53 8093 ]; 27 | networking.firewall.allowedUDPPorts = [ 53 8093 ]; 28 | } 29 | #+END_SRC 30 | 31 | ** Configuration 32 | This is pretty quick. In the [[./docker-compose.yml][docker-compose.yml]] file, simply change the default admin password for the Pi-hole (the =WEBPASSWORD= environment variable defaulted to CHANGEME, you can't miss it) and set the =TZ= variable to your time zone. 33 | 34 | ** Start Container(s) 35 | Once everything is properly configured and you've double-checked it, you can start all the necessary containers by running: 36 | #+BEGIN_SRC sh :noexec 37 | # inside of homelab directory 38 | sudo docker-compose up -d 39 | #+END_SRC 40 | 41 | ** Web Gui Check 42 | There isn't much to setup out of the box, but to check that everything is working properly, navigate to the web portal using the local IP of the server on port 8093 (if server IP is 192.168.1.43, then navigate to http://192.168.1.43:8093). This /should/ pop up with a "403 Forbidden" error, because it doesn't automatically redirect to the admin portal, but this is a quick fix. Simply navigate to the admin page (i.e. http://192.168.1.43:8093/admin/login.php). 43 | 44 | Once logged in, you can configure any settings as you would like, and should see some statistics. 45 | 46 | ** Router Configuration 47 | Before this step, Pi-hole can't do anything to block ads on your devices. In order for it to work, it needs to be the DNS server of your device. To make this apply to the whole network, you must simply set it as the DNS server on your router. 48 | 49 | Find the DNS server settings in your router's configuration, and set the DNS server to "Manual" specifying the IP address of your server (i.e 192.168.1.43) as your DNS server. This should redirect all DNS queries to the Pi-hole on port 53, which will block garbage and only show you the stuff you need. 50 | 51 | Once saving the configuration, test it by going to a couple websites. If you messed up the configuration at all, you may not be able to access /any/ websites (this happens if you can't reach the DNS server). If it's configured correctly, however, you should see less ads and you can now check how many requests have been blocked in the Pi-hole web portal at http://yourlocalip:8093/admin/login.php 52 | 53 | ** Broken Websites 54 | Pi-hole's default configuration will cause some websites to break. Particularly tracking links (from places like Google search) are blocked, which will break links you try to follow after a Google search. [[https://github.com/ClearURLs/Addon][ClearURLs]] browser extension is an easy fix to this. 55 | 56 | ** Extra Blocking 57 | Pi-hole includes only one blocklist by default: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts which does a good job of blocking ads and malware. However, more blocklists are available at his main repo here: https://github.com/StevenBlack/hosts. 58 | 59 | These can be added to Pi-hole here: http://yourlocalip:8093/admin/groups-adlists.php, and downloaded/updated at the "gravity" page: http://yourlocalip:8093/admin/gravity.php. 60 | -------------------------------------------------------------------------------- /homelab/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | nextcloud-db: 5 | image: mariadb:10.5 6 | container_name: nextcloud-db 7 | command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW 8 | restart: unless-stopped 9 | volumes: 10 | - ./data/nextcloud-db:/var/lib/mysql 11 | environment: 12 | - MARIADB_AUTO_UPGRADE=1 13 | - MARIADB_DISABLE_UPGRADE_BACKUP=1 14 | env_file: 15 | - ./env/nextcloud-db.env 16 | networks: 17 | - proxy-tier 18 | 19 | nextcloud-redis: 20 | image: redis:alpine 21 | container_name: nextcloud-redis 22 | restart: unless-stopped 23 | networks: 24 | - proxy-tier 25 | 26 | nextcloud-app: 27 | build: ./builds/nextcloud-app/ 28 | container_name: nextcloud-app 29 | restart: unless-stopped 30 | volumes: 31 | - ./data/nextcloud-app:/var/www/html 32 | environment: 33 | - VIRTUAL_HOST=nextcloud.yourdomain.com 34 | - LETSENCRYPT_HOST=nextcloud.yourdomain.com 35 | - LETSENCRYPT_EMAIL=youremail@domain.com 36 | - MYSQL_HOST=nextcloud-db 37 | - REDIS_HOST=nextcloud-redis 38 | env_file: 39 | - ./env/nextcloud-db.env 40 | depends_on: 41 | - nextcloud-db 42 | - nextcloud-redis 43 | networks: 44 | - proxy-tier 45 | 46 | nextcloud-cron: 47 | image: nextcloud:apache 48 | container_name: nextcloud-cron 49 | restart: unless-stopped 50 | volumes: 51 | - ./data/nextcloud-app:/var/www/html 52 | entrypoint: /cron.sh 53 | depends_on: 54 | - nextcloud-db 55 | - nextcloud-redis 56 | 57 | gitea-app: 58 | image: gitea/gitea:nightly 59 | container_name: gitea-app 60 | environment: 61 | - VIRTUAL_HOST=gitea.yourdomain.com 62 | - LETSENCRYPT_HOST=gitea.yourdomain.com 63 | - LETSENCRYPT_EMAIL=youremail@domain.com 64 | - USER_UID=1000 65 | - USER_GID=1000 66 | - GITEA__database__DB_TYPE=mysql 67 | - GITEA__database__HOST=gitea-db:3306 68 | - SSH_PORT=2321 69 | env_file: 70 | - ./env/gitea-db.env 71 | restart: unless-stopped 72 | networks: 73 | - proxy-tier 74 | volumes: 75 | - ./data/gitea-app:/data 76 | - /etc/timezone:/etc/timezone:ro 77 | - /etc/localtime:/etc/localtime:ro 78 | ports: 79 | - "3000:3000" 80 | - "2321:22" 81 | depends_on: 82 | - gitea-db 83 | 84 | gitea-db: 85 | image: mariadb:10.5 86 | container_name: gitea-db 87 | restart: unless-stopped 88 | env_file: 89 | - ./env/gitea-db.env 90 | networks: 91 | - proxy-tier 92 | volumes: 93 | - ./data/gitea-db:/var/lib/mysql 94 | 95 | syncthing-app: 96 | image: lscr.io/linuxserver/syncthing:latest 97 | container_name: syncthing-app 98 | hostname: yourhostname 99 | environment: 100 | - PUID=1000 101 | - PGID=1000 102 | - TZ=YourRegion/YourCity 103 | networks: 104 | - proxy-tier 105 | volumes: 106 | - ./data/syncthing-app/config:/config 107 | - ./data/syncthing-app/data:/source 108 | ports: 109 | - 8384:8384 110 | - 22000:22000/tcp 111 | - 22000:22000/udp 112 | - 21027:21027/udp 113 | restart: unless-stopped 114 | 115 | freshrss: 116 | image: lscr.io/linuxserver/freshrss:latest 117 | container_name: freshrss-app 118 | environment: 119 | - VIRTUAL_HOST=freshrss.yourdomain.com 120 | - LETSENCRYPT_HOST=freshrss.yourdomain.com 121 | - LETSENCRYPT_EMAIL=youremail@domain.com 122 | - PUID=1000 123 | - PGID=1000 124 | - TZ=YourRegion/YourCity 125 | networks: 126 | - proxy-tier 127 | volumes: 128 | - ./data/freshrss-app/config:/config 129 | restart: unless-stopped 130 | 131 | heimdall-app: 132 | image: lscr.io/linuxserver/heimdall:latest 133 | container_name: heimdall-app 134 | environment: 135 | - VIRTUAL_HOST=homepage.yourdomain.com 136 | - LETSENCRYPT_HOST=homepage.yourdomain.com 137 | - LETSENCRYPT_EMAIL=youremail@domain.com 138 | - PUID=1000 139 | - PGID=1000 140 | - TZ=YourRegion/YourCity 141 | volumes: 142 | - ./data/heimdall-app/config:/config 143 | networks: 144 | - proxy-tier 145 | restart: unless-stopped 146 | 147 | nginx-proxy: 148 | build: ./builds/nginx-proxy/ 149 | container_name: nginx-proxy 150 | restart: unless-stopped 151 | ports: 152 | - 80:80 153 | - 443:443 154 | labels: 155 | com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true" 156 | volumes: 157 | - certs:/etc/nginx/certs:ro 158 | - vhost.d:/etc/nginx/vhost.d 159 | - html:/usr/share/nginx/html 160 | - /var/run/docker.sock:/tmp/docker.sock:ro 161 | networks: 162 | - proxy-tier 163 | 164 | letsencrypt-companion: 165 | image: nginxproxy/acme-companion 166 | container_name: letsencrypt-companion 167 | restart: unless-stopped 168 | volumes: 169 | - certs:/etc/nginx/certs 170 | - acme:/etc/acme.sh 171 | - vhost.d:/etc/nginx/vhost.d 172 | - html:/usr/share/nginx/html 173 | - /var/run/docker.sock:/var/run/docker.sock:ro 174 | networks: 175 | - proxy-tier 176 | depends_on: 177 | - nginx-proxy 178 | 179 | volumes: 180 | certs: 181 | acme: 182 | vhost.d: 183 | html: 184 | 185 | networks: 186 | proxy-tier: 187 | external: true 188 | -------------------------------------------------------------------------------- /setup.org: -------------------------------------------------------------------------------- 1 | #+title: Server Setup 2 | #+author: Emmet 3 | 4 | These are basic instructions for how I have configured the OS and auxiliary packages for my home server. This sets up: 5 | - Sane security defaults 6 | - At-rest encryption for all important data 7 | - Remote and local backups 8 | 9 | For information on how to setup the various Docker containers for services, visit each of the subdirectories here: 10 | - [[./homelab][homelab]] 11 | - [[./network][network]] 12 | - [[./gameservers][gameservers]] 13 | 14 | ** Installation 15 | I use NixOS btw, so I have a NixOS setup for servers. All I need are my dotfiles ([[https://gitlab.com/librephoenix/nixos-config][GitLab]], [[https://github.com/librephoenix/nixos-config][GitHub]]) with =profile= set to "homelab" inside my =flake.nix=. What this essentially does is: 16 | - Activate a firewall that doesn't let anything through by default (except SSH) 17 | - Activates ssh with my public ssh key authorized 18 | - Disallows root login via ssh 19 | - Disallows password-based login via ssh 20 | - Replaces =sudo= with =doas=, since sudo is overkill for a system managed by one person 21 | - Installs necessary packages for server management: 22 | - =docker=, along with docker utilities =docker-compose= and =lazydocker= 23 | - =gocryptfs= for easy at-rest encryption of all important data 24 | - =rdiff-backup= for quick reverse-incremental backups 25 | - =rclone= for connecting to cloud storage for off-site backups 26 | 27 | Again, since I use NixOS, my configuration does all of this for me, since I told it to. All of these can be done on other Linux distros (i.e. Ubuntu, Debian, Arch), but require you to remember a set of steps. A quick look through the [[https://wiki.archlinux.org/][Arch wiki]] should tell you how to set any of these up yourself on a different system if you would like. 28 | 29 | ** At-rest Encryption 30 | Most of the time, when you encrypt a Linux desktop or laptop, you use =block encryption=, which encrypts /literally everything/ (except the bootloader). What this means is, if the computer shuts down unexpectedly, when turning it back on, you must either: 31 | - Input the keyphrase manually 32 | - Store a keyfile on the disk to decrypt it 33 | 34 | In the first case, you /can/ set it up to allow you to input the keyphrase remotely, but that's a bit annoying to setup, which means you can only input the keyphrase when you have physical access to the server. 35 | 36 | In the second case, any moderately skilled hacker who steals your hard drive can decrypt the entire thing, which means the encryption isn't doing its job. 37 | 38 | In any case, while more secure, this block encryption is inconvenient for homelab usage. 39 | 40 | *** Stacked Filesystem Encryption 41 | The other solution is =stacked filesystem encryption=, in which you encrypt only a single directory (we'll call it =crypt=), and then virtually mount it for usage inside a decrypted directory (I usually call this directory =plain=). 42 | 43 | Benefits to this for are: 44 | - You can decrypt it remotely a lot easier (as long as the server is physically on), since you will still always have access to ssh in the case of an interruption 45 | - It's /really/ easy to backup everything to the cloud /encrypted/, since all you need to do is sync the =crypt= folder instead of the =plain= folder 46 | 47 | With respect to block-level encryption: 48 | - It may be easier for an attacker with physical access to tamper with your system-level files to install a rootkit or other nasty hack, since those files remain unencrypted on the system 49 | - With most stacked encryption solutions, attackers can still see the directory structure and relative file sizes of encrypted files, which can allow them to guess what kind of files you have (even if the file names and contents are encrypted) 50 | 51 | For my use I'm going with =stacked filesystem encryption= using a package called [[https://nuetzlich.net/gocryptfs/][gocryptfs]] 52 | 53 | *** Gocryptfs Setup 54 | First create two directories: =crypt= and =plain=. =crypt= is the encrypted directory, while =plain= is where it will be mounted in the clear. 55 | #+BEGIN_SRC sh :noexec 56 | mkdir crypt plain 57 | #+END_SRC 58 | 59 | Then, you must initialize the encrypted directory, providing it with a passphrase: 60 | #+BEGIN_SRC sh :noexec 61 | gocryptfs -init crypt 62 | #+END_SRC 63 | 64 | It will give you a master key, which gives you /complete access to the encrypted directory/. As they explain, save it in a secure place and /only use it in emergencies/! 65 | 66 | Finally, to mount it, you merely run: 67 | #+BEGIN_SRC sh :noexec 68 | gocryptfs crypt plain 69 | #+END_SRC 70 | The only problem with this setup, is that /only the user that called the command can view the unencrypted contents/. If you call this as your normal user /not even root can see inside the decrypted mount/. This is a feature, not a bug. However, since many docker containers rely on multiple users, this means that running most docker containers inside a gocryptfs mount won't work by default. Fortunately, we can allow other users into the mount with the =-allow_others= flag: 71 | #+BEGIN_SRC sh :noexec 72 | gocryptfs -allow_others crypt plain 73 | #+END_SRC 74 | which allows normal filesystem access inside the gocryptfs mount based on *nix permissions. 75 | 76 | ** Container Setup 77 | With gocryptfs setup and mounted, the =docker-compose= examples from this repo can be copied into the =plain= directory and setup. Most examples require you to change defaults, such as database passwords, domain name(s), email, time zones, etc.. More info can be found in each subdirectory (i.e. [[./homelab][homelab]], [[./network][network]], [[./gameservers][gameservers]], etc...) 78 | 79 | Once such defaults are properly configured, you can =cd= into each directory and run: 80 | #+BEGIN_SRC sh :noexec 81 | sudo docker-compose up -d 82 | #+END_SRC 83 | to start the containers. 84 | 85 | ** Backup Setup 86 | If data does not exist in at least 3 physical drives, and at least 2 geographically separated locations, it does not exist. It is a good idea to set up: 87 | - A local backup on a separate SSD or flash drive 88 | - This protects against your main hard drive failing 89 | - Additionally, if it is disconnected, this protects against ransomware and/or other hacks where access to data is compromised by an attacker 90 | - A remote backup in the cloud (or someone else's computer) 91 | - This protects you against a home disaster such as fire, flooding, theft 92 | 93 | More information can be found in [[./homelab/maintenance.org][maintenance.org]] within the homelab directory. 94 | 95 | -------------------------------------------------------------------------------- /homelab/setup.org: -------------------------------------------------------------------------------- 1 | #+title: Homelab Setup 2 | #+author: Emmet 3 | 4 | This walks you through setup of this homelab docker-compose template into a working production environment. 5 | 6 | ** Docker Networking 7 | By default, only docker-compose containers within the same file are able to talk to each other. This can be bypassed by creating an "external" network. In this sample, the external network is called "proxy-tier" and must be created manually with the command: 8 | #+BEGIN_SRC sh :noexec 9 | sudo docker network create proxy-tier 10 | #+END_SRC 11 | 12 | ** Domain Name 13 | In order for this to work, you're going to need a domain name. Do a search for the best domain name registrars and pick one. Then search up domain names you would like until you find something available. Then, as long as you aren't trying to get something fancy like .gg or .ai, the yearly payments shouldn't be too bad. 14 | 15 | Once you have purchased it, you'll need to create an A record for that domain pointing to your IP address (i.e. the IP from your VPS, or the public IP of your home). Then, you'll need CNAME records for each subservices to point to the domain name you chose. Here is an example: 16 | 17 | Domain: example.com 18 | | Type | Host | Value | 19 | |--------------+-----------+---------------| 20 | | A Record | @ | 142.251.32.46 | 21 | | CNAME Record | nextcloud | example.com | 22 | | CNAME Record | gitea | example.com | 23 | | CNAME Record | freshrss | example.com | 24 | | CNAME Record | homepage | example.com | 25 | 26 | The following setup would: 27 | - Redirect example.com to 142.251.32.46 28 | - Redirect nextcloud.example.com (a subdomain of example.com) to whatever example.com is redirected to (142.251.32.46) 29 | - Redirect gitea.example.com (another subdomain of example.com) to whatever example.com is redirected to (142.251.32.46) 30 | - etc... 31 | 32 | As you can see, every single subdomain points to the exact same IP address. However, Nginx Proxy will decide what to show you based on what subdomain you're connecting to. In this sense, you can /only/ connect to the services by supplying the correct subdomain (you can't necessarily connect to your Nextcloud instance via a local IP, like 192.168.1.43). 33 | 34 | ** Port Forwarding 35 | You only need to care about port forwarding if you are setting this up on your home network. Port forwarding redirects traffic on any ports you specify from your public IP address to an internal IP address on your network. On your server, run the following command to get your internal IP address. 36 | #+BEGIN_SRC sh :noexec 37 | ip a 38 | #+END_SRC 39 | 40 | Next, in your router settings, you'll want to first configure the server to have a /static IP address/. Then, you can port forward outside connections to the IP address of your server. 41 | 42 | For this setup, Nginx Proxy requires ports 443 and 80 (443 is for HTTPS while 80 is for HTTP). So to successfully port forward, you'll need 2 rules: 43 | - External Port 443 -> [Server Local IP Address (i.e. 192.168.1.43)] Port 443 44 | - External Port 80 -> [Server Local IP Address (i.e. 192.168.1.43)] Port 80 45 | 46 | Additionally, in this example, Gitea is configured to use SSH on port 2321, so you can port forward that as well: 47 | - External Port 2321 -> [Server Local IP Address (i.e. 192.168.1.43)] Port 2321 48 | 49 | Optionally, for better Syncthing performance, you can configure Syncthing to sync files directly on Port 22000, which would need to be port forwarded for Syncthing to run that way outside of the local network: 50 | - External Port 22000 -> [Server Local IP Address (i.e. 192.168.1.43)] Port 22000 51 | 52 | Remember to keep in mind, /anything you port forward in this manner will be publicly accessible to the internet. If anything that doesn't require authentication is port forwarded, anyone can go and mess with it./ 53 | 54 | ** Firewall Rules 55 | In order to make sure everything works, the server's firewall must be configured to allow access to the ports we need. If you're setting this up on a home network, you may need to make these same configuration changes on your router's firewall as well (many modern routers will automatically update the firewalls once you've set up port forwarding though). 56 | 57 | This part isn't too bad; simply allow incoming access on the ports we mentioned earlier: 58 | - Allow Incoming Port 443 59 | - Allow Incoming Port 80 60 | - Allow Incoming Port 2321 61 | - Allow Incoming Port 22000 62 | 63 | Additionally, you'll need to allow incoming access to port 8384 to control the Syncthing instance on the server. 64 | 65 | In my config ([[https://gitlab.com/librephoenix/nixos-config][GitLab]], [[https://github.com/librephoenix/nixos-config][GitHub]]), all that needs to be set are the rules inside of firewall.nix: 66 | #+BEGIN_SRC nix 67 | { config, pkgs, ... }: 68 | 69 | { 70 | # Firewall 71 | networking.firewall.enable = true; 72 | # Open ports in the firewall. 73 | networking.firewall.allowedTCPPorts = [ 443 80 2321 8384 22000 ]; 74 | networking.firewall.allowedUDPPorts = [ 443 80 8384 22000 ]; 75 | } 76 | #+END_SRC 77 | 78 | If you use something like Ubuntu or Debian, then you probably have [[https://wiki.ubuntu.com/UncomplicatedFirewall][UFW (Uncomplicated Firewall)]] installed. Look at its documentation to find out how to allow access to the necessary ports. 79 | 80 | If you're setting this up on your home network and it doesn't work after you've done all this, check your router settings, since you may have to apply the exact same firewall rules to your router as well. 81 | 82 | ** Configuration 83 | In the [[./docker-compose.yml][docker-compose.yml]] file, configure the following: 84 | - MOST IMPORTANT: CHANGE ALL THE PASSWORDS IN ALL THE ENVIRONMENT FILES (found in the [[./env][env]] directory) 85 | - THEY ALL SAY CHANGEME SO THERE IS NO EXCUSE FOR MISSING THIS 86 | - Set the =VIRTUAL_HOST=, =LETSENCRYPT_HOST= and =LETSENCRYPT_EMAIL= for each service accordingly 87 | - Set the time zone (=TZ=) for the containers that request it 88 | 89 | ** Start Containers 90 | Once everything is properly configured and you've double-checked it, you can start all the necessary containers by running: 91 | #+BEGIN_SRC sh :noexec 92 | # inside of homelab directory 93 | sudo docker-compose up -d 94 | #+END_SRC 95 | 96 | ** Administration Setup 97 | Now, immediately navigate to each public-facing service (Nextcloud, Gitea, FreshRSS, Heimdall). If everything went correctly, you should be greeted with an installation page to complete. This is where you will configure an admin account for the website, so make sure to do it fast before some script kiddy compromises your server! Gitea is a slight exception, as it (more safely) decides to start the setup page on port 3000 which you can access locally (if your local IP is 192.168.1.43, then you can access it on a browser at 192.168.1.43:3000). 98 | 99 | ** Maintenance 100 | For more details on maintaining this environment, see [[./maintenance.org][maintenance.org]]. 101 | --------------------------------------------------------------------------------