# Trent

Welcome to your team’s developer platform

<figure><img src="/files/fLCWV5Kw1m7eZjHkpdJI" alt="Trent and George" width="375"><figcaption></figcaption></figure>

<h2 align="center">Trent Bauer</h2>

<p align="center">Homelab, car and music enthusiast located in Victoria, Australia<br>UTC+10</p>

<p align="center"><strong>Contact me at</strong></p>

{% columns fullWidth="false" %}
{% column %}

<p align="center"><a href="mailto:hello@trentbauer.com">Email</a></p>
{% endcolumn %}

{% column %}

<p align="center"><a href="https://github.com/trentnbauer">Github</a></p>
{% endcolumn %}

{% column valign="bottom" %}

<p align="center"><a href="https://discord.agamersgrind.com/">Discord</a></p>
{% endcolumn %}
{% endcolumns %}

***

<h2 id="h.io2hh4imnqzx_l" align="center">Skillset</h2>

I have developed my skills through my experience at work and self-learning with my Homelab. I have a passion for automation and CX, with a goal of providing easy to use services for colleagues, friends and family.

{% columns %}
{% column %}

* Endpoint Management (Intune)
* Powershell
* Documentation management
* Backup Tools (Veeam, Commvault)
* Change management process
* Cloudflare (DNS & Zero Trust)
* Docker and Docker Compose
  {% endcolumn %}

{% column %}

* Cloud & Virtual Computing
* Infrastructure as Code
* Network Management (UniFi, Meraki, Palo Alto)
* Microsoft 365 (Azure, Sharepoint, Teams etc)
* Pterodactyl / Pelican eggs
  {% endcolumn %}
  {% endcolumns %}

<h2 id="h.fnn1d95dyzd4_l" align="center">Hobbies and Projects</h2>

<h3 id="h.fnn1d95dyzd4_l" align="center">Homelab</h3>

My current homelab consists of a proxmox host, multiple VMs, a Synology NAS and UniFi networking equipment. My services are secured behind Cloudflare tunnels, using Google as an OIDC provider.&#x20;

#### **Infrastructure as Code** <a href="#h.8mgvk9lx2wco_l" id="h.8mgvk9lx2wco_l"></a>

With my most recent rebuild of my homelab, I have gone down the IaC where possible, hosting all of my Docker Compose files in GitHub and deploying them using Portainer stacks. The intention here is to reduce any complexity with rebuilding systems - just spin up a VM, point it at the code and off we go!

<p align="right"><a href="https://www.trentbauer.com/guides/installation-guides/pelican">My favourite IaC stack</a> </p>

#### **Cyber Security** <a href="#h.w8axb3x6c07d_l" id="h.w8axb3x6c07d_l"></a>

I am investing time into the security of my homelab, implementing tools like Crowdsec to automate monitoring and blocking potentially malicious actors.&#x20;

<p align="right"><a href="https://www.trentbauer.com/guides/installation-guides/crowdsec">My Crowdsec guides</a></p>

#### **A Gamers Grind** <a href="#h.w8axb3x6c07d_l" id="h.w8axb3x6c07d_l"></a>

A passion project of mine, AGG originally started as a simple website to publicize my Minecraft server, but has since grown. Over the years I have hosted multiple Minecraft servers (modded and vanilla), Valheim, Insurgency Sandstorm and UT2004 to name a few.&#x20;

<p align="right"><a href="https://agamersgrind.com/">You can view the AGG website here</a></p>

#### Documentation

I have documented and standardized as much as I can in my homelab, which you can view in the tabs to the left. I'm a strong advocate for quality documentation as its a great way to learn but also *to not forget*. I have publicized as much of my Homelabs production configuration as I can, such as my compose files.

<h3 align="center">Other IT related projects</h3>

#### FilmCalc

FilmCalc (Film Calculator) started as a simple webpage to input a film stock details and cost, and lab costs to calculate the cost per photo but it has grown in scope significantly. I am using Claude to code this

<p align="right"><a href="https://filmcalc.trentbauer.com/">FimCalc</a> | <a href="https://github.com/trentnbauer/FilmCalc">Github</a></p>

#### QueueUp

I found out that a close friend of mine had never played Halo (now done) or the Borderlands series (doing) amongst some other games. Queue Up was created for us to add games to a backlog queue. QueueUp has rooms that you can invite your friends to - you can vote on games, get the best price and it has a weighted "next game" system that gives higher priority to games based on genre (not matching last completed game) and votes. It also has a personal backlog tracking system. Again, written with Claude.

<p align="right"><a href="https://github.com/trentnbauer/QueueUp">Github</a></p>

<h3 id="h.fnn1d95dyzd4_l" align="center">Art and Creativity</h3>

#### **Photography**

I have recently started experimenting with photography. My main cameras are Minolta SRTs but I am also collecting Iron Curtain cameras (currently own a Zorki 1b and Zenit C with a Helios 44)

<p align="right"><a href="https://trentbauer.gallery/">Portfolio</a> | <a href="https://instagram.com/_shotbytrent">Instagram</a> | <a href="https://www.lomography.com/homes/trentbauer">Lomography</a></p>

#### **Music** <a href="#h.fnn1d95dyzd4_l" id="h.fnn1d95dyzd4_l"></a>

I have played guitar since I was a young child and spend most of my musical effort on learning 2000's metalcore, such as Killswitch Engage.

***

<h2 id="h.bd3sjfvh8o06_l" align="center">Professional Experience</h2>

<h3 id="h.ubs85r1pkqpd_l" align="center">Local Government</h3>

(2015-Current)

I have worked in government sector for 10+ years in various ICT roles. Some of my achievements are

#### Rewrote New User Script <a href="#h.2j1qwx7gyrw4_l" id="h.2j1qwx7gyrw4_l"></a>

Rewrote the aging New User script and reduced runtime from 15\~ minutes to 1-2 minutes. This has saved approx. 3 weeks of wages per year for the Service Delivery team.&#x20;

In addition to this, I also increased functionality by adding account expiry, building access (done via AD groups) and preconfiguring user MFA details

#### Management of Intune <a href="#h.esszbwnju12q_l" id="h.esszbwnju12q_l"></a>

Creation of application deployments, scripts & remediations, compliance reports and device configurations for Apple iOS and Windows devices

#### Improved Cyber Security posture

I work closely with our CyberSec team, using my knowledge of Intune and Powershell to reduce the risk of CyberSec attacks.

#### 10 years of service

I recieved my 10 years of service award in December 2025

<h3 id="h.5ehdzgnitrw8_l" align="center">MSP</h3>

(2015)

Out of high school I worked at an MSP before moving over to my Government role


# Homelab

## Welcome to my Homelab documenation.&#x20;

In here you will find my live Docker Compose files and flowcharts showing how services link together. If you are looking to replicate any of my config, check out the "guides" section.

{% hint style="info" %}
This documentation is undergoing a major re-write
{% endhint %}


# Docker


# Portainer

## Portainer

[Link to GitHub or Website](https://www.portainer.io/)

Portainer's *hybrid & multi-cloud container management software* supports Kubernetes, Docker, Swarm in any Data Center, Cloud, Network Edge or IIoT Device.

The main instance of Portainer is hosted on Espresso but each other Docker host also has the Portainer Edge Agent installed, which enable central management.

### Flowchart

<img src="/files/ijJjetSCUWiN7GjVxLDG" alt="" class="gitbook-drawing">

As Portainer needs to be installed BEFORE we can use GitOps compose files, we do not use have a Compose file for it.


# GitOps


# 'All' stack

This stack, or a variation of it, is deployed to all of my docker hosts. This contains useful tools such as

* [dockflare](/homelab/service-overviews/security/dockflare)
* [watchtower](/homelab/service-overviews/maintenance-and-monitoring/watchtower)
* [beszel agent](/homelab/service-overviews/maintenance-and-monitoring/beszel)
* and more!

The idea here is to have a standard list of containers deployed to all devices. For example, this stack will automatically enroll any new devices into Beszel for performance monitoring. Watchtower is used to update the Portainer agents

The below compose file is deployed to my VMs

{% @github-files/github-code-block url="<https://github.com/trentnbauer/Homelab/blob/main/docker-compose/all/all-vm.yml>" %}

### And you can find the other variants of the 'all' stack here:&#x20;

{% embed url="<https://github.com/trentnbauer/Homelab/tree/main/docker-compose/all>" %}


# Plex Suite

{% hint style="info" %}
This documentation is undergoing a major re-write
{% endhint %}

Plex is a tool for streaming local content to other devices. The Plex app is available on practically everything that other streaming services are (eg Netflix)

The other apps listed in this doco are here to integrate, manage and automate various systems, such as

* automating quality upgrades
* automatic seasonal content (eg Christmas)
* downloading subtitles and other metadata
* removing unwatched content
* removing stuck downloads or content that cannot be imported
* transcoding x264 to x265 to reduce storage usage

<img src="/files/Zta0brgITxXuh28jxB5k" alt="" class="gitbook-drawing">


# Plex

Plex can be access via [https://plex.tv](https://plex.tv/)

A one-stop destination to stream movies, TV shows, and music, Plex is the most comprehensive entertainment platform available today. Available on almost any device, Plex is the first-and-only streaming platform to offer free ad-supported movies, shows, and live TV together with the ability to easily search—and add to your Watchlist—any title ever made, no matter which streaming service it lives on. Using the platform as their entertainment concierge, 17 million (and growing!) monthly active users count on Plex for new discoveries and recommendations from all their favorite streaming apps, personal media libraries, and beyond.


# Monitarr


# Overseerr

[Link to App](https://github.com/sct/overseerr)

Overseerr is a free and open source software application for managing requests for your media library. It integrates with your existing services, such as Sonarr, Radarr, and Plex!

{% @github-files/github-code-block url="<https://github.com/trentnbauer/Homelab/blob/main/docker-compose/overseerr.yml>" %}


# Plex Suggester


# Wizarr

[Link to Download App](https://github.com/Wizarrrr/wizarr)

Wizarr is a automatic user invitation system for Plex, Jellyfin and Emby. Create a unique link and share it to a user and they will automatically be invited to your Media Server! They will even be guided to download the clients and instructions on how to use your requests software!


# Huntarr


# Bazarr


# Decluttar


# Radarr Schedularr


# Maintainerr


# Prowlarr

[Link to Download App](https://github.com/Prowlarr/Prowlarr)

Prowlarr is an indexer manager/proxy built on the popular \*arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports management of both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Lidarr, Mylar3, Radarr, Readarr, and Sonarr offering complete management of your indexers with no per app Indexer setup required (we do it all).


# Radarr

[Link to Github](https://github.com/Radarr/Radarr)

Radarr is a movie collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new movies and will interface with clients and indexers to grab, sort, and rename them. It can also be configured to automatically upgrade the quality of existing files in the library when a better quality format becomes available. Note that only one type of a given movie is supported.&#x20;

I have a container for 4k content and other for everthing else


# Sonarr

[Link to Github](https://github.com/Sonarr/Sonarr)

Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.

I have a container for 4k content and other for everthing else


# Lidarr


# Tdarr


# Tautulli

[Link to Download App](https://github.com/Tautulli/Tautulli)

A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv/).

This project is based on code from [Headphones](https://github.com/rembo10/headphones) and [PlexWatchWeb](https://github.com/ecleese/plexWatchWeb).


# FlareSolverr

[Link to Download App](https://github.com/FlareSolverr/FlareSolverr)

FlareSolverr starts a proxy server, and it waits for user requests in an idle state using few resources. When some request arrives, it uses [Selenium](https://www.selenium.dev/) with the [undetected-chromedriver](https://github.com/ultrafunkamsterdam/undetected-chromedriver) to create a web browser (Chrome). It opens the URL with user parameters and waits until the Cloudflare challenge is solved (or timeout). The HTML code and the cookies are sent back to the user, and those cookies can be used to bypass Cloudflare using other HTTP clients.

**NOTE**: Web browsers consume a lot of memory. If you are running FlareSolverr on a machine with few RAM, do not make many requests at once. With each request a new browser is launched.

It is also possible to use a permanent session. However, if you use sessions, you should make sure to close them as soon as you are done using them.


# SabNZBD

[Link to GitHub or Website](https://sabnzbd.org/)

SABnzbd is an Open Source Binary Newsreader written in Python.

It's totally free, easy to use, and works practically everywhere. SABnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an `.nzb`. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction. SABnzbd offers an easy setup wizard and has self-analysis tools to verify your setup.


# qBittorrent

[Link to Download App](https://www.qbittorrent.org/)

qBittorrent is a cross-platform free and open-source BitTorrent client written in native C++. It relies on Boost, Qt 6 toolkit and the libtorrent-rasterbar library (for the torrent back-end), with an optional search engine written in Python.


# Dispatcharr


# Games


# Pelican

{% embed url="<https://github.com/pelican-dev/panel>" %}

> Pelican Panel is an open-source, web-based application designed for easy management of game servers. It offers a user-friendly interface for deploying, configuring, and managing servers, with features like real-time resource monitoring, Docker container isolation, and extensive customization options. Ideal for both individual gamers and hosting companies, it simplifies server administration without requiring deep technical knowledge.

For more information, please refer to my guide;

{% embed url="<https://www.trentbauer.com/guides/installation-guides/pelican>" %}

My stack automates some additional items, such as Cloudflare tunnels and Crowdsec

## Panel

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/pelican.yml>" %}

## Wings

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/pelican-wings.yml>" %}


# Infrastructure

{% hint style="info" %}
This documentation is undergoing a major re-write
{% endhint %}


# Cloudflare

I am using Cloudflare for

* Cloudflare Tunnels
* Zero Trust
* Cloudflare Firewall
* DNS


# NextDNS

[Link to GitHub or Website](https://nextdns.io/)

NextDNS protects you from all kinds of security threats, blocks ads and trackers on websites and in apps and provides a safe and supervised Internet for kids — on all devices and on all networks.

This app is hosted externally but there is an application installed on Rigatoni

## Flowchart

<img src="/files/rTO4XG42YGkubPxzYgtL" alt="" class="gitbook-drawing">


# UniFi

UniFi is a line of wireless access points, switches, routers, controller devices, VoIP phones, and access control products, cameras and EV chargers created by Ubiquiti. It can be used for the corporate network and also for the home network. An Unifi network controller manages all the equipment in the UNIFI network.

UniFi devices are managed by the UniFi software, which runs on a Cloudkey device or UniFi OS

## Modifications

[NextDNS agent](https://github.com/nextdns/nextdns/wiki/UnifiOS)

[My Crowdsec UniFi stack](https://www.trentbauer.com/guides/installation-guides/crowdsec/unifi)


# CyberPower PowerPanel & UPS

[Link to GitHub or Website](https://www.cyberpowersystems.com/product/software/power-panel-business/powerpanel-business-windows/)

## PowerPanel Server

PowerPanel® Business software features the most intuitive power management dashboard design on the market. Users can easily monitor and manage CyberPower UPS systems and network-connected PDUs at anytime from anywhere. The user-friendly dashboard provides real-time status at a glance and instant recognition of problems.

{% code title="docker-compose.yml" lineNumbers="true" %}

```yaml
version: "3"

services:
  app:
    image: ghcr.io/nathanvaughn/powerpanel-business:both-490
    privileged: true
    network_mode: host
    ports:
      # Ports: ???, http, https, ???, snmp, snmp
      # See https://dl4jz3rbrsfum.cloudfront.net/documents/CyberPower_UM_PowerPanel-Business-486.pdf
      - 2003:2003
      - 3052:3052/tcp
      - 3052:3052/udp
      - 53568:53568/tcp
      - 53566:53566/udp
      #- 161:161/udp
      #- 162:162/udp
    devices:
      # sharing /dev/usb is sufficient for debian and ubuntu,
      # but other distributions might also need access to
      # /dev/bus/usb/*
      - /dev/usb:/dev/usb
      - /dev/bus/usb:/dev/bus/usb
    restart: unless-stopped
    volumes:
      - app_data:/data
      - /etc/TZ:/etc/TZ:ro

volumes:
  app_data:
    driver: local
```

{% endcode %}

## PowerPanel Remote

PowerPanel Remote is a different version of PowerPanel that is intended for communicating with a server instance for monitoring a UPS. **This is running on the other hardware without USB connectivity to the UPS.**

{% code title="docker-compose.yml" lineNumbers="true" %}

```yaml
version: "3"

services:
  app:
    image: ghcr.io/nathanvaughn/powerpanel-business:remote-490
    privileged: true
    network_mode: host
    ports:
      # Ports: ???, http, https, ???, snmp, snmp
      # See https://dl4jz3rbrsfum.cloudfront.net/documents/CyberPower_UM_PowerPanel-Business-486.pdf
      - 2003:2003
      - 3052:3052
      - 53568:53568/tcp
      - 53566:53566/udp
      #- 161:161/udp
      #- 162:162/udp
    restart: unless-stopped
    volumes:
      - data:/data
      - /etc/localtime:/etc/localtime:ro

volumes:
  data:
    driver: local
```

{% endcode %}

### Flowchart

<img src="/files/PXWkeESb9gGCuVy9c4es" alt="" class="gitbook-drawing">


# Automation, Security, Maintenance & Monitoring

{% hint style="info" %}
This documentation is undergoing a major re-write
{% endhint %}


# Ansible

{% embed url="<https://semaphoreui.com/>" %}

Ansible is a suite of software tools that enables infrastructure as code. It is open-source and the suite includes software provisioning, configuration management, and application deployment functionality

Ansible Semaphore is a modern UI for Ansible. It lets you easily run Ansible playbooks, get notifications about fails, control access to deployment system.

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/ansible.yml>" %}


# Beszel

{% embed url="<https://beszel.dev/>" %}

Beszel is a lightweight server monitoring platform that includes Docker statistics, historical data, and alert functions.

It has a friendly web interface, simple configuration, and is ready to use out of the box. It supports automatic backup, multi-user, OAuth authentication, and API access.

## Beszel Server

{% @github-files/github-code-block url="<https://github.com/trentnbauer/Homelab/blob/main/docker-compose/beszel.yml>" %}

## Beszel Agent

{% hint style="info" %}
The agent is deployed as part of the ['All' stack](/homelab/service-overviews/docker/all-stack)
{% endhint %}

### Beszel Agent with GPU monitoring

{% @github-files/github-code-block url="<https://github.com/trentnbauer/Homelab/blob/main/docker-compose/beszel-agent-gpu.yml>" %}


# Blackbox

{% embed url="<https://github.com/maxjb-xyz/blackbox>" %}

**An intelligent self-hosted forensic event timeline for homelabs and home servers.**\
**Know&#x20;*****what*****&#x20;changed,&#x20;*****when*****&#x20;it changed, and&#x20;*****why*****&#x20;things broke.**

Blackbox is a lightweight, self-hosted event correlation platform built for homelabbers who want to understand their infrastructure at a glance. It collects events from Docker, config file changes, selected systemd units, uptime monitors, and container update tools, correlates them into a single chronological timeline, and groups likely outages into incidents with scored cause candidates and optional local-AI analysis or AI-enhanced correlation.

When your homelab breaks, Blackbox tells you what happened. You don't need to lift a finger.

## Blackbox Server

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/blackbox-server.yml>" %}

## Beszel Agent

{% hint style="info" %}
The agent is deployed as part of the ['All' stack](/homelab/service-overviews/docker/all-stack)
{% endhint %}


# Borg UI

{% embed url="<https://github.com/karanhudia/borg-ui>" %}

**A modern web interface for** [**Borg Backup**](https://borgbackup.readthedocs.io/)\
Run backups, browse archives, restore files, manage repositories, and automate schedules from one interface.

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/borg.yml>" %}


# Prunemate

{% embed url="<https://github.com/anoniemerd/PruneMate>" %}

A sleek, lightweight web interface to **automatically clean up Docker resources** on a schedule. Built with Python (Flask) · Docker SDK · APScheduler · Gunicorn

**Keep your Docker host tidy with scheduled cleanup of unused images, containers, networks, and volumes.**

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/prunemate.yml>" %}


# Watchtower

{% hint style="info" %}
Watchtower is deployed as part of the ['All' stack](/homelab/service-overviews/docker/all-stack)
{% endhint %}

This is used to update the Portainer-CE and Portainer Agent containers


# NetbootXYZ

{% embed url="<https://github.com/netbootxyz/netboot.xyz>" %}

netboot.xyz is a convenient place to boot into any type of operating system or utility disk without the need of having to go spend time retrieving the ISO just to run it. [iPXE](http://ipxe.org/) is used to provide a user friendly menu from within the BIOS that lets you easily choose the operating system you want along with any specific types of versions or bootable flags.

{% @github-files/github-code-block url="<https://github.com/trentnbauer/agg/blob/main/docker-compose/netbootxyz.yml>" %}

| Port | Purpose                       |
| ---- | ----------------------------- |
| 3000 | WebUI                         |
| 69   | DHCP / PXE booting            |
| 80   | File store for bootable media |

| Host Volume | Container Volume | Purpose                          |
| ----------- | ---------------- | -------------------------------- |
| assets      | /assets          | stores downloaded bootable media |
| conf        | /config          | stores configuration files       |

| Integration | Purpose               |
| ----------- | --------------------- |
| UniFi       | Set as PXEBoot server |

\\


# Project & Documentation Management


# LubeLogger

{% @github-files/github-code-block url="<https://github.com/trentnbauer/Homelab/blob/main/docker-compose/lubelogger.yml>" %}


# Security

{% hint style="info" %}
This documentation is undergoing a major re-write
{% endhint %}


# Dockflare

Dockflare is deployed as part of my ['All' stack](/homelab/service-overviews/docker/all-stack) stack


# Google OpenID Auth


# CrowdSec

[Link to App](https://app.crowdsec.net/)

CrowdSec Security Engine, the open-source intrusion prevention system written in Go, protects against attacks on any server by parsing real-time service logs (servers, SSH, WordPress etc. logs) by detecting malicious behaviors.

All our servers are checked for SSH brute force, Log4j exploits and any CVE's that CS can detect. In addition to that, they're configured to monitor logs for the relevant apps on each server, such as attacks against Pterodactyl, Kasm and AdGuard (eg credential brute force).

This app is hosted externally, with a client installed on each VM. This is pushed out via [Ansible](/homelab/service-overviews/maintenance-and-monitoring/ansible)

<table><thead><tr><th width="223.33333333333331">Integration</th><th width="152">Host</th><th>Purpose</th></tr></thead><tbody><tr><td>NFTables</td><td>Any server with a port forward</td><td>Add malicious IPs to firewall blocklists</td></tr><tr><td>SSH</td><td>All servers</td><td>Monitors for bruteforce login attempts</td></tr><tr><td>Cloudflare</td><td>Lungo, Cola, Mocha, Latte</td><td>Provide malicious IP's with Captcha requests before accessing agamersgrind.com, agamersgrind.dev, xfgn.dev and lattemedia.tv</td></tr><tr><td>UptimaKuma</td><td>Cola</td><td>Monitors for brute force login attempts</td></tr><tr><td>AdGuard</td><td>Americano, Espresso</td><td>Monitors for brute force login attempts</td></tr><tr><td>Wireguard</td><td>Cappuccino</td><td>Monitors for bruteforce login attempts</td></tr><tr><td>Pterodactyl Wings</td><td>Cocoa, Mocha</td><td>Monitors for bruteforce login attempts</td></tr><tr><td>Wordpress - AGG</td><td>Cocoa</td><td>Monitors for bruteforce login attempts</td></tr><tr><td>Kasm</td><td>Latte</td><td>Monitors for bruteforce login attempts</td></tr><tr><td>Proxmox</td><td>Macaroni</td><td>Monitors for bruteforce login attempts</td></tr><tr><td>DSM</td><td>Fettuccine</td><td>Monitors for bruteforce login attempts</td></tr></tbody></table>

\\


# Remote Access

Internal and external apps that, in some way, shape or form, allow for Remote Access to the XFGN / AGG / LM servers or services


# Cloudflare Zero Trust

## Zero Trust / Tunnels

Tunnels are managed by [Dockflare](/homelab/service-overviews/security/dockflare)

[Link to App](https://www.cloudflare.com/en-au/products/tunnel/)

The Cloudflare tunnel is a port-forwarding-less reverse proxy. The traffic is tunneled through Cloudflare servers reducing the risk of DDOS and other malicious attacks. Another advantage is that Cloudflare handles authentication, so its harder to brute force one of our internal services.

### Flowchart <a href="#bkmrk-flowchart" id="bkmrk-flowchart"></a>

<img src="/files/5LxCuvxlQI7tJutRubnP" alt="" class="gitbook-drawing">


# Kasm

{% embed url="<https://kasm.com/>" %}

Streaming containerized apps and desktops to end-users. The Workspaces platform provides enterprise-class orchestration, data loss prevention, and web streaming technology to enable the delivery of containerized workloads to your browser.

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/kasm.yml>" %}


# Other Adhoc Apps

{% hint style="info" %}
This documentation is undergoing a major re-write
{% endhint %}


# Home Assistant

[Link to App](https://hassio.xfgn.dev)

[Link to GitHub or Website](https://github.com/home-assistant)

Home Assistant is free and open-source software for home automation designed to be a central control system for smart home devices with a focus on local control and privacy

This app is hosted on Tea, as a virtual machine on Macaroni

## Integrations

| Integration           | Purpose                          |
| --------------------- | -------------------------------- |
| AdGuard               | Pull statistics                  |
| Bureau of Meteorology | Monitor Weather                  |
| CO2 Signal            | Green power usage stats          |
| Glances               | Statistics from Macaroni         |
| Google Cast           | Cast to Google devices           |
| Logitech Harmony      | Monitor and control TV remote    |
| Philips Hue           | Monitor and control Hue lights   |
| Pushover              | Send notifications               |
| Synology DSM          | Monitor and manage the NAS       |
| Thread                | Home Automation Network          |
| TP Link Kasa          | Monitor and control Kasa devices |
| Tuya Cloud            | Monitor and control TUYA devices |
| UniFi                 | Pull stats                       |
| Z-Wave                | Home Automation Network          |
| ZigBee                | Home Automation Network          |

## Backups

Backups are done via the Google Drive backup module for Home Assistant.

It is backed up daily, at 2am. The backups are stored locally and in the XFGNArchives Google Drive account.\\


# EpicGames Free Games

[Link to GitHub or Website](https://github.com/claabs/epicgames-freegames-node)

Automatically login and find available free games the Epic Games Store. Sends you a prepopulated checkout link so you can complete the checkout after logging in. Supports multiple accounts, login sessions, and scheduled runs.

We use the cookie import method to authenticate users.

This app is hosted on Cocoa as a docker container

{% @github-files/github-code-block url="<https://github.com/trentnbauer/agg/blob/main/docker-compose/epicgames.yml>" %}

| Port | Purpose |
| ---- | ------- |
| 3434 | WebUI   |

| Host Volume       | Container Volume | Purpose                        |
| ----------------- | ---------------- | ------------------------------ |
| epicgames\_config | /usr/app/config  | Stores config and cookie files |

## Cookie Import

1. [Follow the official guide to export your cookies](https://github.com/claabs/epicgames-freegames-node?tab=readme-ov-file#cookie-import)
2. Log into [Portainer](/homelab/service-overviews/docker/portainer-and-gitops)
3. Select Cocoa
4. On the left, select Volumes
5. Click on 'Browse' next to the epicgames\_config volume
6. Click on upload
7. Upload your JSON file
8. Edit the config.json file
   1. Download the config.json file
   2. Open the config.json in Visual Studio code
   3. Locate line 35 (accounts), click on the end and then hit enter
   4. Copy and paste the below code block and replace the example email with yours

      ```json
          {
            "email": "example@email.com"
          },
      ```
   5. Save the file
9. Upload the edited config.json file
10. On the left, click on Stacks
11. Select Epic Games
12. Click on 'Stop this stack', then OK
13. Click on 'Start this stack'


# Macaroni

## Specifications

<table><thead><tr><th width="234">Component</th><th>Model</th></tr></thead><tbody><tr><td>Case</td><td>Silverstone Alta G1M White (Planned PowerMac G4 swap)</td></tr><tr><td>Motherboard</td><td>MSI B660M Pro</td></tr><tr><td>CPU</td><td>Intel i5 13500</td></tr><tr><td>CPU Cooler</td><td>Noctua NH-U9</td></tr><tr><td>RAM</td><td>4x32GB Vengeance LP, 3200mhz</td></tr><tr><td>GPU</td><td>Quadro P4000</td></tr><tr><td>Power Supply</td><td>Corsair 600w SFX</td></tr><tr><td>Storage</td><td>2x NVME 2TB SDD<br>2x SATA 4TB HDD<br>1x SATA 128GB SSD</td></tr><tr><td>Other</td><td>HomeAssistant SkyConnect dongle<br>ZWave Dongle</td></tr></tbody></table>

## ZFS Storage Pools

<table><thead><tr><th width="155">Name</th><th width="155">Type</th><th>Drives</th><th>Purpose</th></tr></thead><tbody><tr><td>SSDRAID</td><td>ZFS, MIRROR</td><td>2x 2TB NVME</td><td>VM Disk storage</td></tr><tr><td>HDD-MIRROR</td><td>ZFS, MIRROR</td><td>2x 4TB HDD</td><td>Backup storage</td></tr></tbody></table>

## Images

Images of the server below. Please note: These pictures will span multiple iterations so they hardware may look different between images

Early 2023

![](/files/GJK1d0iBn5gaWgFratST) ![](/files/nlAe6E3CtLIpVz6S301N)

Late 2023

![](/files/4Oc340vl90VUUC3U3KP5) ![](/files/JKm37aGjA7kcvqbAku0O)


# Fettuccine

## Specifications

<table><thead><tr><th width="234">Component</th><th>Model</th></tr></thead><tbody><tr><td>Manufacturer</td><td>Synology</td></tr><tr><td>Model</td><td>DS918+</td></tr><tr><td>CPU</td><td>Intel Celeron J3455</td></tr><tr><td>RAM</td><td>8GB</td></tr><tr><td>Storage</td><td>4x 8TB Seagate Ironwolf Pro's</td></tr></tbody></table>

## Images

<figure><img src="/files/HS6UaCUkfSkGiz2tBFvR" alt=""><figcaption></figcaption></figure>


# Linguine

## Specifications

<table><thead><tr><th width="234">Component</th><th>Model</th></tr></thead><tbody><tr><td>Manufacturer</td><td>Dell</td></tr><tr><td>Model</td><td>Wyse 5070</td></tr><tr><td>CPU</td><td>Intel J5005</td></tr><tr><td>RAM</td><td>4GB SODIMM</td></tr><tr><td>Storage</td><td>1x 16GB SATA NGFF</td></tr></tbody></table>

## Images

<figure><img src="/files/Bzsd2EEIZh6zuMDwZgH1" alt=""><figcaption></figcaption></figure>


# docs.xfgn.dev

*Well, thanks for bookmarking something I wrote - that's pretty cool! Or are you coming from Google?*

## docs.xfgn.dev has moved here

*Welcome to the new doco site!*

I have shut down my old GitBook sites (there were a few\...) and migrated them all into this one. It's a bit of a digital resume / landing page / whatever for me, my homelab and the guides.&#x20;

**As with all things homelab, things are constantly changing, moving and being tweaked.**

#### ⬅️ As before, all the information is on the left

I've dumped a bunch of personally identifiable data and linked this site into my resume-ish sorta thing. This is undergoing a re-rewrite and when completed will have my production / live docker compose files.

#### Is this the same content?

Yes - everything was duplicated from the old site to the new one. It will slowly be re-written and updated though.


# Guides

{% hint style="info" %}
Use the menu on the left to pick a guide! Mobile users will need to open the hamburger menu on the top left
{% endhint %}

{% hint style="warning" %}
Due to a limitation in Gitbook, some drawings may not display properly in dark mode - **it is recommended to view this doco in light might.**

[I've logged it with Gitbook](https://github.com/orgs/GitbookIO/discussions/1122#discussioncomment-15298819)
{% endhint %}

#### G'day and welcome to my guides!&#x20;

You'll find various bits of useful information here. I've written this for myself, as these particular apps are either

* Annoying to configure
* Not well documented online or
* Are stable enough for me to forget how to configure them, but not stable enough to last forever (looking at you Pterodactyl)
* ... Or my friends have asked me how to replicate something

#### **A piece of advice,**

**You will never have 99% uptime** and people accessing your homelab are not entitled to your time either. If your Plex server is down, they can use Netflix. The 'luxury' of paying money for those services is uptime and support.&#x20;

*Don't make your homelab your second job.*

{% hint style="warning" icon="siren-on" %}
The compose files referenced in the guides are my LIVE compose files. Generally speaking, they will be functional, but if you notice recent changes (eg in the last couple hours) there may be something I'm changing or troubleshooting.If so, the files may not work.&#x20;

**I highly recommend copying my compose files into your own Github and managing them yourself from there**.

If you do use my live compose files in your set up, please ensure you

* Do not set your pull time to something quick (eg 5 minutes) - set it to something like 24 or 48 hours. This reduces the risk of me testing, troubleshooting or upgrading containers from breaking your containers
  {% endhint %}

## How this documentation is written

### Sectioned documentation

*We're all busy and can't dedicate 3 straight hours to 1 task.*

Where logical, I have split my doco into multiple pages and a page should be completed in a single sitting. I've tried to keep each page to less than 30 minutes of work. This means that you can complete a page, look after your kids, then come back and do another.

### Errors

If I'm expecting an error to occur, I'll write it into the doco. Otherwise, refer to the troubleshooting section on the left and/or Google.

## Some things to consider

Here are some basic things to consider for your Homelab

### Password vault

Save all of your data into a password vault - a lot of API keys cannot be re-viewed after being generated and rolling them will break any services that use them

### [Use SSH keys](/guides/installation-guides/ssh-keys)

SSH keys allow for password-less authentication - the device connecting requires the username and key and off it goes.

### Authentication

**I'm using OAuth with a third party service.** At the end of the day, I trust Google, Github, Facebook etc to have better security standards than an authentication provider written by someone as a hobby. These tools won't have the ability to protect against complex attacks or DDOS.&#x20;

The self hosted solutions are usually open source, which means green hats can review and report vulnerabilities, it also means they're availabe for bad actors to find and exploit.

Whwn I have to set a username and password, I'm randomly generating both and putting them behind Cloudflare - hopefully with both [Cloudflare Zero Trust](/guides/other-guides/cloudflare/cloudflare-zero-trust) AND [Cloudflare security rules](/guides/installation-guides/crowdsec/cloudflare-security-rules)

### Managing updates

I'm using [GitOps](/guides/installation-guides/gitops) to manage my container updates. It is best practice to not use the "latest" tag as these may be dev/testing builds or come with breaking changes.

### Cyber Security

Cyber security should be high on your thoughts list, especially if you are port forwarding services. I'm in the process over overhauling the CyberSec in my Homelab and am writing documentation in [Crowdsec](/guides/installation-guides/crowdsec) for this.

### Don't store important data on your homelab

**Another controversial take, but this one comes with a story.**

Previously I had hosted Bitwarden as a docker container in my homelab. I ended up deciding to pay the $10ish a year to support the devs and shifted my vault over to their cloud version.&#x20;

I had forgotten to update an old device pointing at the local version and when I accessed that device it couldn't authenticate. I did some digging and realized that the front-end of the container wasn't loading. The container and its database had died some time ago and I didn't have a backup to restore from.

I was lucky as I had already shifted my data away.

### Backups

Back up your important data somewhere - preferebly offsite.&#x20;

### Don't force other people to use your homelab

Your homelab is your hobby - not everyone else is going to want to use it. Sure, make it available, but be aware of what may go wrong if something breaks. You don't want to have a drive die and then lose all of your families photos.


# SSH Keys

{% hint style="warning" %}
THIS GUIDE IS A WORK IN PROGRESS
{% endhint %}


# Portainer

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>1 Hour</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>SSH</td></tr></tbody></table>

##


# Portainer

## Install Portainer

1. SSH into the server you wish to install Portainer on
2. Copy paste the below commands to install Docker (assumes Ubuntu)

   <pre class="language-bash" data-line-numbers><code class="lang-bash"> sudo apt-get update
    sudo apt-get install ca-certificates curl gnupg
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    sudo chmod a+r /etc/apt/keyrings/docker.gpg
   echo \
     "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
     "$(. /etc/os-release &#x26;&#x26; echo "$VERSION_CODENAME")" stable" | \
     sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
   sudo apt-get update
   sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin
   </code></pre>
3. Run the below command to create your Portainer container

   ```bash
   docker run -d --label=com.centurylinklabs.watchtower.enable=true -p 8000:8000 -p 9443:9443 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer:/data portainer/portainer-ce:latest
   ```

   *I would suggest installing Portainer using this command (rather than the one provided by Portainer themselves) as it adds a WatchTower label to enable auto-updates, which we will set up later in this doco*
4. Run the below command and confirm you see that the Portainer container is running

   ```bash
   docker up
   ```

## Configure Portainer

Now that Portainer is installed, we can browse to the webUI and configure it

### Account Creation

1. Browse to `https://YOURSERVERIP:9443`
2. Create your credentials
   1. Save them to your password vault

## Create a stack to update Portainer

This will deploy a Watchtower container which will update any apps with the label `com.centurylinklabs.watchtower.enable=true` . This allows for automatic updates for the Portainer container

1. Navigate to stacks
2. Create a new stack and provide the below code<br>

   ```yaml
   version: '3'
   services:
     app:
       image: ghcr.io/containrrr/watchtower:latest
       restart: always
       volumes:
         - /var/run/docker.sock:/var/run/docker.sock
       environment:
         - TZ=$TZ
         - WATCHTOWER_ROLLING_RESTART=true
         - WATCHTOWER_CLEANUP=true
         #- WATCHTOWER_DEBUG=true
         - WATCHTOWER_INCLUDE_STOPPED=true
         - WATCHTOWER_POLL_INTERVAL=86400
         - WATCHTOWER_RUN_ONCE=true
         - WATCHTOWER_LABEL_ENABLE=true
       #command: --label-enable
       logging:
         driver: "json-file"
         options:
           max-size: "10m"
           max-file: "3"
   ```
3. Click on Deploy

{% hint style="warning" %}
This is NOT a recommended solution for updating containers. This guide will only assist you with updating Portainer, as it is potentially a public facing resource and needs to be patched. The WatchTower auto update may (and probably will) break Portainer at some stage. Keep backups.
{% endhint %}


# Agents

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>30 minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>Portainer, SSH, Docker Compose</td></tr></tbody></table>

## Portainer Agents

Portainer Agents allow different machines to talk back to and be managed by a single instance of Portainer. This is useful if you are segregating your network, have multiple VMs or physical devices.

The advantages of a set up like this are;

* Portainer Edge can browse Docker Volumes. The main Portainer installation cannot browse volumes, thus you cannot download, edit and upload config files from the browser
* Segregating functions,
  * eg a VM for publically available services and one for internally available services

### Install Portainer Edge Agents

1. On the left hand pane, click on Environments
2. On the far right, click on 'Add environment'
3. Select 'Docker Standalone' and click on 'Start Wizard'
   1. Click on 'Edge Agent Standard'
   2. Give the instance a name, such as the host name for the server / machine
   3. The Portainer API server URL should be something like:

      ```
      yourserverIP:9443
      ```

      \
      *If possible, use your servers DNS alias here to make your network more resilient to IP address changes*
   4. Click on Create
   5. Scroll down and locate the 'Docker Standalone' install script and copy this to notepad
      1. Ensure the last line is '`portainer/agent:latest`' and not versioned (eg agent:10.1)
      2. Replace the the 'docker run' line with the below. This will allow WatchTower to automatically update the Edge Agent

         ```bash
         docker run -d --label=com.centurylinklabs.watchtower.enable=true \
         ```
   6. SSH into your server that will have the Edge Agent installed and log in as the Root account with the below command

      ```bash
      sudo -i
      ```
   7. Copy paste the below commands to install Docker (assumes Ubuntu)

      ```sh
       sudo apt-get update
       sudo apt-get install ca-certificates curl gnupg
       sudo install -m 0755 -d /etc/apt/keyrings
       curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
       sudo chmod a+r /etc/apt/keyrings/docker.gpg
      echo \
        "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
        "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
        sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
      sudo apt-get update
      sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin
      ```
   8. Install the Edge Agent by copy pasting the text copied and altered in step 5
   9. Wait for the container to download and launch
   10. Click on 'Home' and confirm all your agent's show a green Heartbeat
4. Repeat step 3 for any other Edge Agent's you're configuring

### Create Tags & Groups

1. On the left hand panel, click on Environments
2. Click on Tags
3. Create any relevant tags, such as 'Test' or 'Production'
4. On the left, click on Groups
5. Click on Add Group
   1. Provide a name, eg 'Test' or 'Production'
   2. Provide a description, eg 'Test servers'
   3. Click on tags and select the relevant tag
   4. Click on 'Create group'
6. Repeat step 5 for any other groups

#### Apply Tags

1. On the left hand menu, click on Home
2. Click on the pen icon next to your first Portainer instance
3. under Metadata
   1. Under Group, select the relevant group (eg 'test')
   2. Under Tags, select the relevant tag/s (eg 'test', 'linux')
4. Click on Update environment
5. Repeat for each Portainer instance

### Enable Edge Compute

1. On the left hand panel, click on Settings
2. Click on 'Edge Compute'
3. tick 'Enable Edge Compute features'

### Set up the WatchTower Edge stack

1. On the left, click on 'Edge Stacks' and click on 'Create'
   1. Name your stage 'WatchTower'
   2. Click on Edge Groups and select all your groups
   3. In the web editor, provide the below compose file and hit deploy

      ```yaml
      version: '3'
      services:
        app:
          image: ghcr.io/containrrr/watchtower:latest
          restart: always
          volumes:
            - /var/run/docker.sock:/var/run/docker.sock
          environment:
            - TZ=$TZ
            - WATCHTOWER_ROLLING_RESTART=true
            - WATCHTOWER_CLEANUP=true
            #- WATCHTOWER_DEBUG=true
            - WATCHTOWER_INCLUDE_STOPPED=true
            - WATCHTOWER_POLL_INTERVAL=86400
            - WATCHTOWER_RUN_ONCE=true
            - WATCHTOWER_LABEL_ENABLE=true
          #command: --label-enable
          logging:
            driver: "json-file"
            options:
              max-size: "10m"
              max-file: "3"
      ```
2. Take note of the state of your stacks\
   ![](/files/NywHHLrcL1QjgwRfAuEa)
3. Click on 'WatchTower' and you will be shown the same screen as step 1. You can make any adjustments to the stack here, such as removing groups or editing the compose file
4. Click on the Environments tab. This will show any server the stack has been deployed too and their state. The stack will be deloyed to any devices in the groups chosen in step 1.2

This stack will enable auto-updates for anything with the `com.centurylinklabs.watchtower.enable=true` label

{% hint style="warning" %}
This is NOT a recommended solution for updating containers. This guide will only assist you with updating Portainer, as it is potentially a public facing resource and needs to be patched. The WatchTower auto update may (and probably will) break Portainer at some stage. Keep backups.
{% endhint %}


# Stacks (docker compose)

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>30 Minutes</td></tr><tr><td>Difficulty</td><td>Low - Moderate</td></tr><tr><td>Required Knowledge</td><td>Portainer, Docker compose</td></tr></tbody></table>

## What is a Stack?

Stacks is how Portainer handles Docker Compose files, which are infrastructure as code documents for spinning up multiple containers, volumes and networks in 1 go.

Most docker container developers now provide example compose files with their projects. Its also possible to Google and find examples online

***

For example, you may have a service that has a separate container for

* *a database*
* *a web app*
* *back end compute*

*The database and back end compute may be on Network 1, while the web app is on Network 2.*

*All 3 containers have access to the same 'config' volume, but the database and webapp have their own unique volumes*

*The database and backend need the same credentials to the 2 systems can talk, but the web app has separate credentials for the gui. These are environmental variables*

***

Instead of manually setting up and inputting environmental variables, networks and volumes for each container this can all be written into the compose and and span up in 1 click. This also makes the set up system agnostic.

## Deploy your Stack

{% tabs %}
{% tab title="GitOps Private" %}
{% hint style="info" %}
In this example, we're going to deploy the Ombi compose file we created in [Create your first compose file](/guides/installation-guides/gitops/create-your-first-gipops-compose-file)
{% endhint %}

1. Log into Portainer
   1. If you set up Edge Agents, click on the host you want Ombi to exist on
2. On the left hand menu, select 'Stacks'
3. Click on 'Add stack'
   1. Give your stack a name (eg "ombi")
   2. Build method = Repository
   3. Tick 'Authentication'
      * Username = Github Email
      * Personal Access Token = PAC saved in [Create your GitHub Repo](/guides/installation-guides/gitops/create-your-github-repo)
   4. Repository URL = the URL of your repo + .git (eg "<https://github.com/trentnbauer/agg.local.git\\_"\\>\_)
   5. Compose path = docker-compose/ombi.yml
   6. Tick 'Automatic updates'\
      You should see a page similar to this
   7. Scroll down to the 'environment variables' section and add the following variables

      * TZ
      * BASE\_URL
      * PORT\_HTTP

      <figure><img src="/files/zs4wtELZlivHinX5Hd63" alt=""><figcaption></figcaption></figure>
   8. Provide your TZ per the ['tz identifier' here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
   9. Provide a base url, if you are using one (not required)
   10. Provide the port that Ombi will use
   11. Click on 'Deploy the stack'
       {% endtab %}

{% tab title="GitOps Public" %}
{% hint style="info" %}
In this example, we're going to deploy the Ombi compose file we created in [Create your first compose file](/guides/installation-guides/gitops/create-your-first-gipops-compose-file)
{% endhint %}

1. Log into Portainer
   1. If you set up Edge Agents, click on the host you want Ombi to exist on
2. On the left hand menu, select 'Stacks'
3. Click on 'Add stack'
   1. Give your stack a name (eg "ombi")
   2. Build method = Repository
   3. Repository URL = the URL of your repo + .git (eg "<https://github.com/trentnbauer/agg.local.git\\_"\\>\_)
   4. Compose path = docker-compose/ombi.yml
   5. Tick 'Automatic updates'\
      You should see a page similar to this
   6. Scroll down to the 'environment variables' section and add the following variables

      * TZ
      * BASE\_URL
      * PORT\_HTTP

      <figure><img src="/files/zs4wtELZlivHinX5Hd63" alt=""><figcaption></figcaption></figure>
   7. Provide your TZ per the ['tz identifier' here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
   8. Provide a base url, if you are using one (not required)
   9. Provide the port that Ombi will use
   10. Click on 'Deploy the stack'
       {% endtab %}

{% tab title="Copy paste / write in Portainer" %}
{% hint style="info" %}
While this is easier than using GitOps, its no where near as powerful and will require manual intervention to update containers.

I do this often when I am building compose files, as its easier to work with the native editor as aposed to GitHub
{% endhint %}

1. Log into Portainer
   1. If you set up Edge Agents, click on the host you want Ombi to exist on
2. On the left hand menu, select 'Stacks'
3. Click on 'Add stack'
   1. Give your stack a name (eg "ombi")
   2. Build method = Web editor
4. Paste your compose file into the web editor and make any adjustments required
   {% endtab %}
   {% endtabs %}

If you have any issues, refer to the Notifications tab at the top of the page;

<figure><img src="/files/hH8ayQ0PaCzdOevraSeg" alt=""><figcaption></figcaption></figure>

Most Portainer errors can be resolved with a simple Google search but I also have my own [troubleshooting list here](https://github.com/trentnbauer/agg-docs/blob/main/guides/portainer-and-gitops/broken-reference/README.md)

### Updating Stacks

If you have used GitOps, Portainer will check in with GitHub and download the updated compose file and update the container image.

You can tell Portainer to manually pull by;

1. Browse to the stack
2. Click on 'Pull and redeploy'

If you have not used GitOps you will need to edit the stack yourself as needed


# GitOps

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>1 - 2 Hours</td></tr><tr><td>Difficulty</td><td>Low</td></tr><tr><td>Required Knowledge</td><td>Docker, Docker Compose, GitHub</td></tr></tbody></table>

## The Scenario

Our goal is to make use of GitOps (a variation of DevOps, using GitHub) to have a infrastructure-as-code set up, allowing us to easily manage and deploy changes with minimal touch and 1 source of truth.

Whilst Portainer is a GUI based application, it can read code from GitHub which allows it to automatically push changes based on our code.

The lifecycle of the container (starting, stopping and deleting) is still managed via the Portainer GUI.

### Prerequisites

* [A machine with Portainer installed](/guides/installation-guides/portainer)
* A GitHub account


# Create your GitHub Repo

## Set up your repo

{% hint style="info" %}
If you have a pre-existing repo you wish to use, feel free to skip this section
{% endhint %}

Your Github repo will be where you store your docker compose files, and anything else you wish to store alongside it. Placing these files in Github makes the risk of losing your compose files to a dead harddrive impossible.

You can also store your Ansible playbooks in there too [Ansible](/guides/installation-guides/ansible)

### Create a Github repo

[Follow GitHubs documentation on creating a repo](https://docs.github.com/en/get-started/quickstart/create-a-repo)

[Recommended: Set your repo as private](https://docs.publishing.service.gov.uk/manual/make-github-repo-private.html)

{% hint style="info" %}
I would recommend creating a Private repo as this hides it from the public internet. The advantage of this is it reduces the risk of leaking credentials, API keys etc if you accidentally save them into a file on the repo.\
\
The best way to secure your repo is to use variables and [secrets](https://docs.github.com/en/actions/concepts/security/secrets)
{% endhint %}

{% hint style="danger" %}
Version history is always there, so if you accidentally save a password or API key its saved forever. The only way to remove it is to delete the repo.
{% endhint %}

## If you are using a private repo,

{% hint style="info" %}
If you are using a public repo, skip this section
{% endhint %}

### Create a Private Access Token for Portainer

As your repo is private, you will need to create a PAC for Portainer to use and access the repo

1. Click on your profile in the top right, then select Settings
2. On the left, click on Developer Settings
3. Click on Personal Access Tokens, then 'Tokens (classic)'
4. Click on 'Generate new token', then select 'Classic'
5. Input the below information

   <figure><img src="/files/gaOURSO7eviFXA7GO52k" alt=""><figcaption><p>You can expire the credential if you want, though this may break Portainer</p></figcaption></figure>
6. Scroll down and click on 'Generate Token'
7. Save your PAC to your password vault


# Container Updates

## Install Renovate Bot

The Renovate Bot watches for dependancies and automatically creates merge requests to update the contents of your Repo. This allows you to update your containers outside of Portainer as well as review changes made etc.

### Get your Repo ready

1. Create a folder '.github' in the root of your repo
2. Create a folder 'docker-compose' in the root of your repo
3. In the '.github' folder, create a file 'renovate.json5' with the below contents

```json5
{
    "$schema": "https://docs.renovatebot.com/renovate-schema.json",
    "extends": [
        "config:base",
        ":disableRateLimiting"
    ],
    "docker-compose": {
        "fileMatch": ["docker-compose/.+\\.ya?ml$"]
    }
}
```

{% hint style="info" %}
This code block tells the bot to watch any '.yml' or '.yaml' file in the 'docker-compose' folder

Feel free to use a different directory or the root of the repo
{% endhint %}

### Install the Bot

1. Follow [this link ](https://github.com/marketplace/renovate)to install the bot
2. You can set renovate to run on all repo's you own, or only your repo created in this doco\
   \&#xNAN;*This is up to you. If you actually use GitHub for development, it may be best to select only this repo*

### Confirm the bot is installed

1. Browse to your GitHub repo
2. Click on 'Issues', you should see the Dependency Dashboard\
   ![](/files/efCWiXaESBZSYl8cnJSc)

### Updating the Compose file

When a new update is released, the renovate bot will create a pull request in your repo;

<figure><img src="/files/UNySxELvIOT14FSjjWZm" alt=""><figcaption><p>the 'home page' of a pull request</p></figcaption></figure>

We can see that this pull request is updating the package 'mariadb'. It is a minor update, going from version 10.5 to 10.11

Clicking on the 'Files changed' tab will list any files that will be updated;

<figure><img src="/files/YGvbDaz5uHNpOE8okTuU" alt=""><figcaption><p>this pull request will replace the red line with the green line</p></figcaption></figure>

If we are happy with this update, we can click on the 'Merge pull request' button at the bottom of the homepage


# Create your first compose file

## Start with the developers compose file

In this section we will create a compose file for the [Omnitools app](https://github.com/iib0011/omni-tools)

1. Open your GitHub repo and enter the docker-compose folder
2. Click on 'Add file' > 'Create new file'
3. Name the file 'omnitools.yml', and copy the compose file [from here](https://docs.linuxserver.io/images/docker-ombi)\
   It should look something like this:

   ```yaml
   services:
     omni-tools:
       image: iib0011/omni-tools:latest
       container_name: omni-tools
       restart: unless-stopped
       ports:
         - "8080:80"
   ```
4. Hit 'Commit changes...'
5. hit 'Commit changes'
   1. If you care, write some information about what you are doing
6. Navigate to your file and review its contents

## Update the compose file

Copy and pasting other peoples compose files is great and all, but you are best to learn how to write and edit them yourself.

### Variables

To keep your compose files as system agnostic as possible, its best to use variables where you can. Some suggestions are

* Ports
* Volume paths
* Labels

These variables can then be set in Portainer when creating the stack

### Health Checks

Some containers come with health checks, some do not. After starting the stack, refer to the state. If it is "running", it does not have an inbuilt health check.

#### Website

If your app has a web-ui, you can test the below commands

1. Open up your stack and select the container
2. Click on the Console button
3. Attempt each command until you get access to the terminal
   1. Click on the command dropdown and select the first option
   2. Click on connect
   3. Repeat until you have access to the terminal
4. Once you are connected, try the below commands. If you get "not found" or something similar, move onto the next one
   1. `curl`
   2. `wget`&#x20;
5. Add the below to your app. **Do not forget to update the port**

{% tabs %}
{% tab title="Curl" %}

```
healthcheck:
  test: curl --connect-timeout 15 --silent --show-error --fail -k http://localhost:80
  interval: 30s
  retries: 3
  start_period: 30s
  timeout: 20s
```

{% endtab %}

{% tab title="WGET" %}

```
healthcheck:
  test: wget --no-verbose --tries=1 --spider http://LOCALHOST:3000 -O /dev/null || exit 1
  interval: 30s
  retries: 3
  start_period: 15s
  timeout: 25s
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
You may need to adjust the retries, start period and timeout
{% endhint %}

#### Database

#### Labels

#### Volumes

### Container versioning

Do not use the 'latest' tag, as it does not allow Renovate to update the compose file, and if it automatically updates it may bring braking changes.

When versioning your compose files, locate the 'latest' tag on the DockerHub, GitHub etc and use the relevant version number.

<figure><img src="/files/7TDQyDADCQmrDHTmP0A1" alt=""><figcaption></figcaption></figure>

This is harder to do on DockerHub. Per the screenshot below, we've located the 'latest' tag and used the digest version to locate the correct version.

<figure><img src="/files/WU973byunxvdUFbo8xOn" alt=""><figcaption><p>Take note of the OS/ARCH as well - you can see that tag 2.12.5 contains both AMD64 and ARM64 containers, meaning this compose file can be used on both ARM and x86 machines.</p></figcaption></figure>

### Here is my tweaked example

{% @github-files/github-code-block url="<https://github.com/trentnbauer/Homelab/blob/main/docker-compose/omnitools.yml>" %}

{% hint style="info" %}
Please note: The `dfgeneric` labels relate to the Dockflare app - [DockFlare (Tunnel management)](/guides/other-guides/cloudflare/dockflare-tunnel-management)
{% endhint %}


# Pterodactyl

## Pelican Panel

{% hint style="warning" %}

#### **I have moved over to Pelican.**

Pelican is a fork of Pterodactyl that is currently in beta. It has some pretty cool features like OAuth and supports plugins, [You can view a comparison of Pelican vs Pterodactyl here](https://pelican.dev/docs/comparison/)

**Please look at my** [Pelican](/guides/installation-guides/pelican) **guide**

This guide is kept online as an archive and is NOT maintained
{% endhint %}

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><strong>Total Time Required</strong></td><td>1 Hour</td></tr><tr><td><strong>Difficulty</strong></td><td>Moderate</td></tr><tr><td><strong>Required Knowledge</strong></td><td>Docker, Docker Compose, Reverse Proxies, DNS, Dockflare</td></tr></tbody></table>

## The Scenario

Our goal is to create a new Pterodactyl Panel and Wings node for hosting game servers.

**This documentation is NOT intended for a professional / reseller environment.** Please do not follow this guide if you intend on selling or publicizing your server resources, as

* Proxy / Tunnelling wings and panel is NOT supported by Pterodactyl developers
* This guide is provided with best effort support

{% hint style="info" %}
*Further testing has confirmed the panel will ONLY talk to DB's on 3306*
{% endhint %}

## Prerequisites

* [ ] A machine to host the panel
  * [ ] [Docker and compose installed](https://docs.docker.com/engine/install/ubuntu/)
  * [ ] Port 3306 to be available

    <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><em>Further testing has confirmed the panel will ONLY talk to DB's on 3306</em></p></div>
* [ ] A machine to host the wings node

  <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><em>You can create multiple node machines and load balance your servers, our scale outwards in the future</em></p></div>

  * [ ] [Docker and Compose installed](https://docs.docker.com/engine/install/ubuntu/)
  * [ ] A high performance, well spec'd machine;
    * Lots of RAM
    * [High single thread passmark CPUs](https://www.cpubenchmark.net/cpu_list.php) (required for Minecraft)
    * SSD storage
* [ ] A Domain that's managed by Cloudflare
* [ ] [Cloudflare Zero Trust tunnel](/guides/other-guides/cloudflare/cloudflare-zero-trust)
* [ ] [Dockflare container](/guides/other-guides/cloudflare/dockflare-tunnel-management)
* [ ] [A Cloudflare DDNS container](/guides/other-guides/cloudflare/dynamic-dns)
  * [ ] With Proxy disabled

### Recommended

* [ ] [Portainer & GitOps](/guides/installation-guides/gitops)
* [ ] A separate server for the Panel and each Wings node (if setting up more than 1)

### Panel

The Pterodactyl Panel is the front end gui for managing your servers. The panel can be connected to multiple Wings nodes (or hosts), which this documentation is written for.

### Wings

Wings hosts the game server compute (CPU) and storage. As this machines job is to process data, a lot of high performing single thread cores are required as well as a lot of RAM.

### Flowchart

<figure><img src="https://4115153834-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F6s9LbmHPE41U6jxE7Mp6%2Fuploads%2FLIrMkgJVCxZY3X6E6Z20%2Ffile.excalidraw.svg?alt=media&#x26;token=492c8a46-ed18-4945-a4b2-b34ff40cbdf4" alt=""><figcaption><p>As you can see, Wings and Panel communicate via Cloudflare, over the internet. This is not great but OK for a homelab</p></figcaption></figure>


# Creating a Panel

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>15 Minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr></tbody></table>

## Installing the Panel

### Setting up the Portainer stack

Create your Portainer stack using the below compose and .env file

{% @github-files/github-code-block url="<https://github.com/trentnbauer/agg/blob/main/docker-compose/pterodactyl-panel.yml>" %}

{% code title=".ENV File" %}

```editorconfig
# --- Domain Settings ---
DOMAIN=example.com
SUBDOMAIN=panel.
TZ=UTC

# --- Networking ---
PORT_HTTP=80
# This should be your internal DNS / IP address
HOSTNAME=localhost

# --- Database ---
# These must match what is set in the 'database' service
MYSQL_PASS=
MYSQL_PASS_ROOT=

# --- Mail (SMTP) ---
MAIL_FROM=noreply@example.com
MAIL_SERVER=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your-email@gmail.com
MAIL_PASS=your-app-password

# --- Security ---
#Must be a random 32 character string - use your password generator
HASHIDS_SALT=

# --- Cloudflare Tunnel ---
CFPOLICY=bypass
CFACCESSNAME=Pterodactyl Panel
CFDURATION=8h
```

{% endcode %}

### Confirm the Panel is running

Check the Portainer logs for the panel container, you should see something similar to below

```
external vars exist.
Checking if https is required.
Checking if letsencrypt email is set.
No letsencrypt email is set using http config.
Removing the default nginx config
Checking database status.
Waiting for database connection...
database (172.28.0.3:3306) open
Migrating and Seeding D.B
   INFO  Nothing to migrate.  
   INFO  Seeding database.  
  Database\Seeders\NestSeeder ........................................ RUNNING  
  Database\Seeders\NestSeeder ................................... 2.34 ms DONE  
  Database\Seeders\EggSeeder ......................................... RUNNING  
*********************************************
*     Updating Eggs for Nest: Minecraft     *
*********************************************
Updated Paper
Updated Bungeecord
Updated Vanilla Minecraft
Updated Sponge (SpongeVanilla)
Updated Forge Minecraft
*************************************************
*     Updating Eggs for Nest: Source Engine     *
*************************************************
Updated Counter-Strike: Global Offensive
Updated Garrys Mod
Updated Team Fortress 2
Updated Ark: Survival Evolved
Updated Insurgency
Updated Custom Source Engine Game
*************************************************
*     Updating Eggs for Nest: Voice Servers     *
*************************************************
Updated Teamspeak3 Server
Updated Mumble Server
****************************************
*     Updating Eggs for Nest: Rust     *
****************************************
Updated Rust
  Database\Seeders\EggSeeder .................................. 175.67 ms DONE  
Starting cron jobs.
Starting supervisord.
2023-06-13 13:41:50,854 CRIT Server 'unix_http_server' running without any HTTP authentication checking
```

#### Confirm the Login page loads

Browse to [http://yourserver:port](https://www.trentbauer.com/guides/installation-guides/pterodactyl/http:/yourserver:port) and confirm you see the below

<figure><img src="/files/o9zxp8nzI5YBXokRd4jw" alt=""><figcaption></figcaption></figure>

## Create your Admin user

1. Open up Portainer and navigate to the Panel container
2. Click on Console and change the command to '/bin/sh'
3. Hit Connect
4. Input the below command and next through the prompts (set account as administrator)

```sh
php artisan p:user:make
```

5. Log into Pterodactyl with your newly created administrator account

## Configure the Panel

### Enforce 2FA

1. Click on the Settings cog in the top right hand corner
2. Click on Settings
3. Set 'Require 2FA authentication' to 'All Users' and hit Save
4. Click on 'Enable 2FA' and follow the steps
5. Save your backup codes somewhere

### Create Node Locations

1. Click on Locations, then 'create new'
2. Create 2 locations,
   1. a location for 'On Prem' nodes
   2. a location for 'Off Prem' nodes

## Confirm the Proxy address loads

{% hint style="info" %}
As you have configured the Panel using Dockflare, you do not need to take any additional steps for this.
{% endhint %}

Browse to the ${SUBDOMAIN}${DOMAIN} you set in your [.env file](/guides/installation-guides/pterodactyl/creating-a-new-panel#setting-up-the-portainer-stack) and ensure the page loads. If it was freshly created, you may need to wait some time for DNS to sync.

Refer to the [DockFlare (Tunnel management)](/guides/other-guides/cloudflare/dockflare-tunnel-management) webui to confirm it is applying


# Creating a Wings node

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>20 Minutes</td></tr><tr><td>Difficulty</td><td>Moderate</td></tr></tbody></table>

{% hint style="info" %}
I am installing Wings on my test VM, 'Basil', for writing this documentation. Any references to Basil will need to be changed to your server. Once this documentation has been finalized, Wings will be deleted from Basil.
{% endhint %}

## Installing Wings and set up Reverse Proxy

### Configure the 'Pterodactyl Wings' Portainer stack

Refer to the [Portainer](https://github.com/trentnbauer/agg-docs/blob/main/guides/pterodactyl/broken-reference/README.md) and [GitOps](https://github.com/trentnbauer/agg-docs/blob/main/guides/pterodactyl/broken-reference/README.md) documentation for configuring a new Portainer stack using the AGG Pterodactyl Wings Docker Compose file

* Name the stack something simple, like 'wings'
* Ensure you fill all the variables from the ENV file

{% @github-files/github-code-block url="<https://github.com/trentnbauer/agg/blob/main/docker-compose/pterodactyl-wings.yml>" %}

{% code title=".ENV file" %}

```editorconfig
# --- General Settings ---
TZ=UTC

# --- Networking Ports ---
PORT=443
PORT_SFTP=2022
PORT_DB=3306
# This should be your internal DNS / IP address
HOSTNAME=localhost

# --- Database Credentials ---
# Replace these with strong passwords
SQL_PASS=
SQL_PASS_ROOT=

# --- Cloudflare Tunnel Configuration ---
CFDOMAIN=example.com
CFSUBDOMAIN=wings.
CFPOLICY=bypass
CFACCESSNAME=Pterodactyl Wings
CFDURATION=8h
```

{% endcode %}

### Allow Ports through the Firewall

1. SSH into your server
2. Allow SSH and enable the firewall with the below commands

   ```
   ufw allow ssh
   ufw enable
   ```

   \
   \&#xNAN;*This is to increase security on this hardware as a port forward is required*
3. Run the command `ufw allow PORT`, replacing PORT with what you set for PORT and PORT\_SFTP above, eg;

   <pre class="language-sh"><code class="lang-sh"><strong>ufw allow 2022
   </strong></code></pre>

### Test the reverse proxy works

{% hint style="info" %}
As you have configured the Panel using Dockflare, you do not need to take any additional steps for this.
{% endhint %}

Browse to the ${SUBDOMAIN}${DOMAIN} you set in your [.env file](/guides/installation-guides/pterodactyl/creating-a-new-panel#setting-up-the-portainer-stack) and ensure the page loads - you should see something similar to below. If it was freshly created, you may need to wait some time for DNS to sync.

```
{"error":"The required authorization heads were not present in the request."}
```

## Configure Panel & Wings

### Set up a Node on the Panel

1. Log into Pterodactyl with an administrator account
2. Click on the Settings cog in the top right
3. Click on Nodes
4. Click on Create New in the top right and
   * Name your Node (I normally give them the same name as the server, or node1)
   * Provide the FQDN of your Node (this is the externally available reverse proxy address configured here [#configure-the-reverse-proxy](#configure-the-reverse-proxy "mention"))
   * Tick 'Use SSL Connection'
   * Tick 'Behind Proxy'
   * Provide RAM & RAM over allocation of the machine or VM
   * Provide the disk space & over allocation of the machine or VM
   * Set your Daemon port to 443 (As Cloudflare uses 443 for its proxy)
   * Click on 'Create Node'

     <figure><img src="/files/O9BRuswpjyTGjDyJWFGj" alt=""><figcaption></figcaption></figure>

### Upload Configuration file to Wings

1. In Pterodactyl, click on 'Nodes' on the left
2. You should see your new Node, with a red heart

   <figure><img src="/files/YjECS8il6xZjoYzcaoWf" alt=""><figcaption></figcaption></figure>
3. Click on your new node, then the Configuration tab and confirm that the highlighted lines are the same as mine

   <figure><img src="/files/A84z72mV1heG28aCgBYh" alt=""><figcaption><p><em>I would also recommend changing the upload_limit to '1024'</em></p></figcaption></figure>

   *If the highlighted settings are NOT the same, you made a mistake in step* [#set-up-a-node-on-the-panel](#set-up-a-node-on-the-panel "mention")*. Delete the node and start back there*
4. Copy the contents of the file and save it as 'config.yml'

{% tabs %}
{% tab title="Using Portainer Edge Agent" %}

1. Log into Portainer and click on the Wings host
2. Click on 'Stacks' and select the 'wings' stack
3. Click on 'Stop stack'
4. Click on 'Volumes'
5. Locate the Wings 'config' volume - it will be named after your stack (eg wings\_config) and click on Browse
6. Click on the Upload button, then select your config.yml file
   {% endtab %}

{% tab title="Not using Edge Agent" %}

1. SSH onto your Wings Node
2. Run the command `docker volume list` and locate the Wings Config volume
3. Run the command docker `volume inspect <VolumeNameHere>` to get the mountpath of the volume
4. CD to the mountpath
5. run command `nano config.yml` and paste in the contents of the config file
6. Press `CTRL + O`, then `Enter` to save the config file
   {% endtab %}
   {% endtabs %}

### Restart the container to load the config file

1. Click on Stacks and open your Wings stack
2. Restart the stack and wait 30 seconds
3. Refresh the Pterodactyl Nodes page and it should now be connected

   <figure><img src="/files/HIhZ6QWPTZjjEbgOVkyM" alt=""><figcaption></figcaption></figure>

### Assigning Ports to the Node

1. Click on your new node and click the 'Allocations' tab
2. On the right hand panel, input
   1. IP address: 0.0.0.0
   2. IP Alias: Your domain or subdomain that points to your servers public IP
   3. Ports: The port range you will forward to this VM (I've chosen ports 500-600)\\

      <figure><img src="/files/As31FrI615wxaZsvZbK3" alt=""><figcaption></figcaption></figure>
   4. Run the `ufw allow` command to allow these ports through the firewall. You can use a : to allow a range, such as 500:600

### Port Forward

You will now need to port forward the chosen ports to your server.

Firstly, have a look at your modem / router. You will need to take note of

* Brand
* Model number or part number

Port forwarding is particularly tricky for beginners, mostly because every device is slightly different.

You'll need to do some Googling and / or Youtube'ing on how to port forward with your model router / modem.

## Why do I do it this way?

My assumption here is that the Pterodactyl dev's want us to set up and use SSL certs on the Wings host and make it publicly available without a proxy. I can't be bothered generating certs and managing their expirations in my Homelab. My configuration offloads the certificate management to Cloudflare (or any other reverse proxy - I used to use NGINX Proxy Manager for this) which means that I don't need to worry about cycling certificates on the host / containers themselves.

Offloading this task to the Cloudflare tunnel does create a couple of potential issues,

* If the tunnel is down / crashes, the Panel can't talk Wings
* If your hosting the Panel and Wings in your homelab, the connection is reliant on the internet being up
* Cloudflare outages may break the Panel / Wings communication
* Pterodactyl Discord does not provide support for proxied panel/wings connections

{% hint style="info" %}
I don't have SFTP working with this set up, though I imagine changing the SFTP port and forwarding that port to the host, then connecting via `ddns_address:port` would work.
{% endhint %}


# Configuring your Node Database

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>10 minutes</td></tr><tr><td>Difficulty</td><td>Low</td></tr></tbody></table>

## Purpose of this database

This database is separate from the Panel's database and included in my Wings compose file. This ensures that the data stored is segregated from the Panel DB (reducing risk of corrupting the panel) and allowing for the game server to connect to a DB on the same host, which will result in better performance.

## Create the Wings DB user

Firstly, we need to create the Wings user. This is unfortunately a manual step that needs to be done on the DB but its pretty straight forward

1. Log into Portainer and navigate to Stacks
2. Open the Pterodactyl Panel stack and take note of the MYSQL\_PASS\_ROOT variable
3. Use a password manager to generate a password for the 'Wings' user, please ensure you save these credentials
4. Scroll down and click on the database container
5. Click on Console, then Connect
6. Type `mysql -u root -p`, hit enter and input the Root password from step 2
7. Copy and paste the below text into Notepad, and change update the password field

   ```sql
   CREATE USER 'wings'@'%' IDENTIFIED BY '<<YOUR PASSWORD HERE>>';
   GRANT ALL PRIVILEGES ON *.* TO 'wings'@'%' WITH GRANT OPTION;
   exit
   ```
8. Copy and paste each individual line into Portainer and hit enter

*This will create a 'wings' user that can connect from anywhere ( % ) with admin privileges... Keep these creds safe!*

## Add the Database into Pterodactyl

Now we need to add the Database into Pterodactyl. Luckily - this is much easier to do!

1. Log into your Pterodactyl Panel as an administrator
2. Hit the settings cogs / admin button in the top right
3. On the left, click on Databases
4. Fill out the database per below and hit Create

| Field        | Data                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------ |
| Name         | A friendly name for easy viewing purposes. I recommend using the same as the nodes hostname.     |
| Host         | Your server hostname / IP                                                                        |
| Port         | The [PORT\_DB stack variable](/guides/installation-guides/pterodactyl/creating-a-new-wings-node) |
| Username     | wings                                                                                            |
| Password     | The password you generated above                                                                 |
| Linked Nodes | The Node the database is stored on                                                               |

*Please note: Your Wings nodes will need to be able to comminute with your Database. This means that you will need them in the same LAN / Network, or some form of VPN or port forwarding (not recommended...) to allow them to communicate.*

## Test creating a Database

1. Open an existing game server, or create a new Minecraft server (please ensure it has a database limit of more than 1)
2. Click on Databases
3. Click on 'New database'
4. Give it a name and hit 'Create'
5. Click on the eye to view the database details, such as the username, password and connection string. This information is needed to configure your game server to use the DB


# Creating your first game server

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>Unknown</td></tr><tr><td>Difficulty</td><td>Moderate</td></tr></tbody></table>


# Best practices & suggestions

## What is this page?

This is a page I've created to provide to friends and family with access to create, manage and host servers on my Pterodactyl instance. It's a basic catch all for things like managing access, backups and files.

## Schedules

### Backup

### Server Reboot

Most servers like to be rebooted every 24 hours. Some (especially heavily modded Minecraft servers) should be rebooted multiple times a day

## Granting access to other people


# Troubleshooting

##


# Pelican

> Welcome to the first 2026 guide!

{% hint style="info" %}
I'm in the process of creating a Crowdsec collection for monitoring the Panel for brute-force sign in attempts and block them at Cloudflare.

*If you're interested in this, please reach out.*
{% endhint %}

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><strong>Total Time Required</strong></td><td>2 Hours</td></tr><tr><td><strong>Difficulty</strong></td><td>Easy - Moderate</td></tr><tr><td><strong>Required Knowledge</strong></td><td>Docker Compose, DNS, Cloudflare Zero Trust, Linux Servers</td></tr></tbody></table>

<img src="/files/GGNkELyeGRnbglQR71Bh" alt="" class="gitbook-drawing">

## What does this guide do?

This guide will help you set up Pelican panel and supporting applications.

In short, this guide will have you

* Create an API key in Cloudflare for
  * Dynamic DNS
  * Zero trust tunnels
  * Crowdsec integration
* Set up Cloudflare Turnstile (Captcha)
* Set up your Cloudflare security rules
  * Force captcha for potentially malicious IPs
  * Blocking known malicious IPs
* Deploy your compose stacks with env files for
  * Panel
  * Node (can be repeated for multiple nodes)
* Configure Crowdsec
  * Subscribe to IP lists
  * Block brute force attacks
  * Block known malicious addresses
  * How to unblock IPs if required
* Set up your port ranges
  * A port range for game servers (externally available)
  * A port range for internal only services (such as a database)

I've had a focus on security and ease of use for these 2 stacks. Using the correct env values (covered later), these compose files will automatically build a strong and secure hosting environment for your game servers. I've done this by using initialization containers to build and download configuration files, and Crowdsec for security.

[My repository for the Crowdsec configurations](https://github.com/trentnbauer/HomelabPublic/tree/main/crowdsec)

<table><thead><tr><th width="212">Container</th><th>Function</th><th data-hidden></th></tr></thead><tbody><tr><td>Node / Wings</td><td>The software that hosts and manages game servers. You can connect multiple Wings nodes to a singular panel</td><td></td></tr><tr><td>Panel / Pelican</td><td>The front-end UI, used to manage multiple nodes</td><td></td></tr><tr><td>Dockflare</td><td><p>Dockflare is a IaC solution for managing Cloudflare Tunnel routes using Docker labels. <br> This compose stack uses the dockflare label prefix <code>dfpelican</code></p><div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p>If you are using Dockflare elsewhere in your homelab, I would highly advise changing the label prefix as you may experience issues with this compose file taking over other Dockflare tunnels<br><br>The version of Dockflare used will grab ANY container with the <code>dockflare</code> or <code>cloudflare.tunnel</code>  labels, even though we've set the prefix</p></div></td><td></td></tr><tr><td>Cloudflare Tunnel</td><td>The "bridge" between your internal network and Cloudflare servers. Web apps are tunnelled through this, allowing a public address to reach an internal resource. Access is secured with CF Zero Trust<br><br><em>The tunnel is created by Dockflare outside of the compose file - you will need to manually delete it if you delete the compose stack</em></td><td></td></tr><tr><td>Crowdsec Engine</td><td><p>Crowdsec is a crowdsourced cyber security tool. It monitors the Wings SFTP logs for brute force attempts adds the IP address to a 4 hour blocklist.</p><p>It also adds known bad IPs to your Cloudflare firewall, forcing them to do a Captcha before accessing anything.<br><br>If a device is incorrectly blocked, refer to <a data-mention href="/pages/SfxKUnWiIUSSzQga747x#delete-a-false-positive">/pages/SfxKUnWiIUSSzQga747x#delete-a-false-positive</a></p></td><td></td></tr><tr><td>Crowdsec Blocklist</td><td>This container will add some of Crowdsec Paid blocklists to your engine for free<br><a href="https://github.com/wolffcatskyy/crowdsec-blocklist-import">https://github.com/wolffcatskyy/crowdsec-blocklist-import</a></td><td></td></tr><tr><td>Firewall Bouncer</td><td>This is the 'bridge' between the Crowdsec Engine container and the firewall on your machine</td><td></td></tr><tr><td>Cloudflare Bouncer</td><td>This is the 'bridge' between Crowdsec Engine and your Cloudflare Security Rules</td><td></td></tr></tbody></table>

## Prerequisites

Please ensure you meet these before continuing.

### Requirements

* [ ] A Linux based machine or VM with
  * [ ] [Docker installed](https://docs.docker.com/engine/install/ubuntu/)
* [ ] [A Domain that's managed by Cloudflare](/guides/other-guides/cloudflare/configure-domain)
* [ ] [Cloudflare Zero Trust tunnel](/guides/other-guides/cloudflare/cloudflare-zero-trust)
* [ ] A text editor
* [ ] Not behind a CGNat
* [ ] A dynamic IP is OK \*\*\*

### Recommended

* [ ] [Manage your Compose Files in GitHub](/guides/installation-guides/gitops) (this manages updates too)
* [ ] A decently spec'd machine to host the servers on
  * [ ] [High single thread passmark CPUs](https://www.cpubenchmark.net/cpu_list.php)
  * [ ] Lots of RAM
  * [ ] SSD storage
* [ ] [Gmail SMTP account](/guides/other-guides/google/gmail-smtp)
* [ ] [A free / community Crowdsec account](https://app.crowdsec.net/signup)
* [ ] Read [Guides](/guides)

## Before you start,

You will need to select some subdomains and ports. Take note of the below table and note your information in a txt document.

<table><thead><tr><th width="217">Data</th><th width="211">Example</th><th>Explaination</th></tr></thead><tbody><tr><td>Domain</td><td>example.com</td><td>The main domain hosting your game servers</td></tr><tr><td>Subdomain for Panel</td><td>panel </td><td>the subdomain your panel will be hosted on, eg panel.example.com</td></tr><tr><td>Subdomain for node</td><td>node1 *</td><td>The address your wings node will be available at. The panel will communicate with this address to manage it</td></tr><tr><td>Subdomain for game servers and SFTP</td><td>play ***</td><td>The DNS address that players will use to join your servers, eg play.example.com<br><br>This uses a Cloudflare dynamic DNS container, so the IP is automatically updated.</td></tr><tr><td>Game server port range</td><td>6600-6700 *</td><td>A port range that your players will join</td></tr><tr><td>Internal port range</td><td>8600-8700 **</td><td>A port range that internal services are hosted on</td></tr><tr><td>From email address</td><td>noreply@example.com</td><td>The email address that will send panel alerts from, eg password reset link</td></tr><tr><td>SFTP Port</td><td>2022 *</td><td>The SFTP port admins can use to upload content to their servers. This is required due to the 100MB upload limit applied to Cloudflare tunnels</td></tr></tbody></table>

{% hint style="info" %}
\* If you are setting up multiple nodes, the subdomain and port range for each node must be unique. If your nodes are on the same Public IP, you will need a unique SFTP port per node.

\*\* Your internal port range must be higher than your game port range. This is because Pelican will default to the LOWEST available port when creating a server. I recommend setting it quite a bit higher (eg 1000) than the game port range.\
The internal port range does not need to be unique per node\
\
\*\*\* If your nodes have different public IPs you will need a different join / play subdomain
{% endhint %}


# Cloudflare

To make DDNS, Dockflare and Cloudflared work we need some data from Cloudflare

#### Before continuing, please follow

1. [Configure Domain](/guides/other-guides/cloudflare/configure-domain) and
2. [Cloudflare Zero Trust](/guides/other-guides/cloudflare/cloudflare-zero-trust)

{% hint style="danger" %}
Please ensure you set the [Cloudflare Zero Trust](/guides/other-guides/cloudflare/cloudflare-zero-trust#set-up-wildcard-application) - if you do not, external parties will be able to access and modify your Dockflare settings - this is a major security risk - as well as your database contents if you enable the Adminer proxy link
{% endhint %}

### Generate your API key

This API key will be used for Dockflare, Dynamic DNS and Crowdsec

1. Navigate to <https://dash.cloudflare.com/profile/api-tokens>
2. Click Create Token > Custom Token
3. Name your token Pelican, and set the below&#x20;
   1. permissions

      <table><thead><tr><th width="148"></th><th width="357"></th><th></th></tr></thead><tbody><tr><td>Account</td><td>Cloudflare Tunnel</td><td>Edit</td></tr><tr><td>Account</td><td>Account Filter Lists</td><td>Edit</td></tr><tr><td>Account</td><td>Firewall Access Rules</td><td>Edit</td></tr><tr><td>Account</td><td>Account Settings</td><td>Read</td></tr><tr><td>Account</td><td>Access: Apps and Policies</td><td>Edit</td></tr><tr><td>User</td><td>User Details</td><td>Read</td></tr><tr><td>Zone</td><td>DNS</td><td>Read</td></tr><tr><td>Zone</td><td>Firewall Services</td><td>Edit</td></tr><tr><td>Zone</td><td>Zone</td><td>Edit</td></tr></tbody></table>
   2. Account Resources

      | Field   | Data        |
      | ------- | ----------- |
      | Include | All Account |
   3. Zone Resources\
      *You can have additional domains*

      |         |               |             |
      | ------- | ------------- | ----------- |
      | Include | Specific Zone | Your Domain |
   4. Click on Continue to Summary
4. Save your API key to your notepad, `CF_APITOKEN=`

### Get your Account ID

1. Browse to <https://dash.cloudflare.com/>
2. Next to your name, click on the 3 dots and select Copy Account ID
3. Save to your notepad, `CF_ACCOUNTID=`

### Get your Zone ID

1. Browse to <https://dash.cloudflare.com/?to=/:account/home/domains>&#x20;
2. Click manage next to your domain
3. Scroll down and locate "API" on the right
4. Save  your Zone ID to your notepad, `CF_ZONE_ID=`

## Security rules

Configure some security rules to reduce the risk of malicious actors accessing your domain

1. Navigate to <https://dash.cloudflare.com/>
2. Select your domain
3. On the left, click expand Security and select rules
4. Click on create rule > custom rule
   1. Next to Expression Preview, click on 'edit expression' to get the free text field
5. Create a rule for each of the below

### Block bots

This policy will show a Captcha challenge to any IPs suspected of botting

<table><thead><tr><th width="229">Field</th><th>Data</th></tr></thead><tbody><tr><td>Rule Name</td><td>Block Bots</td></tr><tr><td>Expression</td><td>(cf.client.bot)</td></tr><tr><td>Choose action</td><td>Managed Challenge</td></tr><tr><td>Place at</td><td>First</td></tr></tbody></table>

### Challenge Threat Score

These IPs are potentially malicious. These addresses will be prompted for Captcha

<table><thead><tr><th width="229">Field</th><th>Data</th></tr></thead><tbody><tr><td>Rule Name</td><td>Challenge Threat Score</td></tr><tr><td>Expression</td><td>(cf.threat_score gt 10)</td></tr><tr><td>Choose action</td><td>Managed Challenge</td></tr><tr><td>Place at</td><td>Custom - after 'Block Bots'</td></tr></tbody></table>

### Block Threat Score

These IPs are very likely to be malicious. These addresses will be blocked

<table><thead><tr><th width="229">Field</th><th>Data</th></tr></thead><tbody><tr><td>Rule Name</td><td>Challenge Threat Score</td></tr><tr><td>Expression</td><td>(cf.threat_score gt 50)</td></tr><tr><td>Choose action</td><td>Block</td></tr><tr><td>Place at</td><td>Custom - after 'Challenge Threat Score'</td></tr></tbody></table>

{% hint style="info" %}
An additional rule will be created by the Crowdsec CF Bouncer container after the Compose file is ran
{% endhint %}


# Crowdsec key

Crowdsec is a crowdsourced cyber security tool, used to automate blocking malicious actors.&#x20;

#### Enrolling in Crowdsec allows for some advanced functionality,

* Downloading managed blocklists
* Uploading IPs that your Crowdsec instance has identifed as malicious or risky
  * This helps other people using Crowdsec and will also show you stats on if other Crowdsec engines have seen this IP
* A nice UI to view and manage your blocked IP addresses
* Some email basic based reporting

## Create your Crowdsec account

> I set up Crowdsec a long time ago, so I can't remember the process, sorry. I am planning on writing documentation for this but haven't gotten around to it yet.

1. Sign up for a Crowdsec account, and select community / free tier when prompted

## Get your Enrollment key

1. Navigate to <https://app.crowdsec.net/>
2. Click on 'Enroll',
   1. copy the enroll key to your notepad


# Shared env file

Fill the below env file with information gathered so far. This env file will be added onto the Panel and Wings env files later.

```dotenv
# --- General Settings ---
TZ=
DOMAIN=

# --- Cloudflare & Dockflare ---
CF_APITOKEN=
CF_ACCOUNTID=
CF_ZONE_ID=

# --- Proxy Addresses ---
PANEL_SUBDOMAIN=

# --- Crowdsec ---
CROWDSEC_ENROLL_KEY=
```


# Panel

Below you will find my live Pelican compose file. I recommend placing this in your Github repo and use [GitOps](/guides/installation-guides/gitops) to manage updating the container versions

## Deploy the Compose File

1. Combine the [Shared env file](/guides/installation-guides/pelican/shared-env-file) with below and fill the blanks

```dotenv
# --- Passwords ---
## The longer the better. Feel free to use passphrases
MYSQL_ROOT_PASS=
MYSQL_PASS=

# --- Mail Configuration ---
## Defaults to Gmail - you can review the compose file for the variables and edit for other SMTP servers if needed
MAIL_FROM=
MAIL_USERNAME=
MAIL_PASSWORD=
```

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/pelican.yml>" %}

### Set and backup your app key

1. Open the logs for the Panel container
2. Take note of your App Key - save it into your password vault and add it to your .env as `APP_KEY=`
3. Restart the panel container

{% hint style="warning" %}
This is to reduce the risk of [Pterodactyl / Pelican](/guides/troubleshooting/pterodactyl-pelican#the-mac-key-is-invalid) bricking your panel - if its occurs and you don't have the key, you will need to reinstall Pelican
{% endhint %}

## Generate Database and Administrator account

1. Enter the Console for the Panel container, by using either docker exec or by using your flavour of Docker webUI
2. Run the below command to generate the database tables and a generic admin account

   ```bash
   php artisan p:environment:setup
   php artisan migrate --force
   php artisan p:user:make --email="noreply@example.com" --username="admin" --password="admin" --admin=1

   ```

{% hint style="warning" %}
These credentials need to be changed ASAP
{% endhint %}

3. Navigate to your panel URL
4. Log in with the generic admin credentials created above (admin/admin)
5. Click on the profile picture in the top right, then select profile
6. Update the username and password to be random generated
7. Save these in your password vault
8. Update the email address

{% hint style="info" %}
As the front end of the panel is public facing, it is best to randomly generate BOTH the username and password to reduce the risk of someone breaking in
{% endhint %}

## Panel Configuration

### Fix the proxy address

1. Click on the profile picture in the top right, then select admin
2. On the left, select settings
3. Under Trusted Proxies, click on 'Set to Cloudflare IPs'
4. Add `192.168.253.0/24`&#x20;

   <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>This is the IP range of the Pelican management network, which contains the Cloudflare tunnel container.</p><p>This resolves some upload failure errors</p></div>
5. Click on save

### Create your user roles

1. Navigate to your panel URL and access the admin side
2. On the left, select Roles

These are my settings:

{% tabs %}
{% tab title="Administrator" %}
Tick everything!
{% endtab %}

{% tab title="User" %}

<figure><img src="/files/P6sLK05vaegJX91y3PJ6" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

{% hint style="info" %}
My settings may not work for your use-case, but they will be a good starting point
{% endhint %}

### Set up Gmail SMTP

1. Create your Gmail SMTP credentials per [Gmail SMTP](/guides/other-guides/google/gmail-smtp)
2. Log into the Pelican admin panel
3. Select settings, the mail
   1. Set to STMP
   2. Add your details
   3. Click on test

### Set up OAuth

1. On the left, click on settings, then Oauth
2. Configure any OAuth methods you wish to use - Pelican will provide you with steps on how to configure each one.\
   *I recommend Discord and Steam*\
   *I would also recommend enabling automatic linking but not account creation*
3. Click on Save

### Cloudflare Turnstile (Captcha)

1. Click on the Captcha tab and expand turnstile
2. Navigate to <https://www.cloudflare.com/en-au/application-services/products/turnstile/#turnstile-pricing>
3. Select the free version\
   You may need to go through some sort of sign up process
4. Click on Add Widget
5. Name your widget (Pelican Panel is fine)
6. Click on Add hostname
   1. Add a custom hostname for your Panel domain
7. Widget mode: managed
8. Click on Create
9. Copy the site and secret key provided into Pelican Panel
10. Click on save

{% hint style="info" %}
Sometimes appears to not save the OAuth details - restart your panel if so
{% endhint %}


# Wings

## Generate your Wings configuration file

1. Navigate to your panel URL
2. Log in with administrator credentials
3. Click on the profile picture in the top right, then select admin
4. On the left, click on Nodes
5. Click on the + in the top right hand corner

   | Field Name           | Data                       |
   | -------------------- | -------------------------- |
   | Domain Name          | the URL for the Wings node |
   | Port                 | 443                        |
   | Display Name         | Whatever you want!         |
   | Communicate over SSL | HTTPS Reverse Proxy        |
   | Port                 | 8080                       |
6. Click on the next arrow and set the below

   | Field Name   | Data                                                                                          |
   | ------------ | --------------------------------------------------------------------------------------------- |
   | Upload Limit | 99 (this limit is set by Cloudflare Zero Trust free)                                          |
   | SFTP Port    | The SFTP port you selected earlier                                                            |
   | SFTP Alias   | <p>The domain your players will join<br><em>SFTP does not have the 99mb upload limit</em></p> |
7. Set your memory, disk and CPU allocations if wanted
8. Click on the + symbol in the bottom right to create the config file
9. Click on the 'Auto Deploy Command' button and take note of
   1. Token
   2. Node ID
10. Close the auto deploy command panel, then click on Save
11. On the left, click on Nodes

{% hint style="success" %}
You will see your node listed with an orange / red heart
{% endhint %}

## Deploy the Compose File

1. Combine the [Shared env file](/guides/installation-guides/pelican/shared-env-file) with below and fill the blanks

```dotenv
# --- Wings Configuration ---
NODE_SUBDOMAIN=
PANEL_TOKEN=
NODE_ID=
SFTP_PORT=

# --- Crowdsec Configuration ---
CROWDSEC_KEY=AGreatSpotToPutAUniquePassphrase123
CROWDSEC_PORT=8080

# --- Domain addresses ---
JOIN_SUBDOMAIN=join
```

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/pelican-wings.yml>" %}

## Confirm the config has applied

### Check the logs for you init-wings container and look for

```bash
Successfully configured wings.
```

#### Common errors

```bash
panic: invalid character '<' looking for beginning of value
```

Your Dockflare container has not generated the public bypass app for the wings address. Assuming that Dockflare is OK (refer to its logs),

1. Stop the Wings stack
2. Delete the dockflare volume
3. Start the stack

Unable to resolve or DNS error

1. Wait half an hour for DNS to update
2. Start the init container

You may need to do this a few times

### Open your Wings container and look for

<pre class="language-log"><code class="lang-log">                     ____
__ Pelican _____/___/_______ _______ ______
\_____\    \/\/    /   /       /  __   /   ___/
   \___\          /   /   /   /  /_/  /___   /
        \___/\___/___/___/___/___    /______/
                            /_______/ 1.0.0-beta21
Copyright © 2018 - 2026 Dane Everitt &#x26; Contributors
Website:  https://pelican.dev
 Source:  https://github.com/pelican-dev/wings
License:  https://github.com/pelican-dev/wings/blob/main/LICENSE
This software is made available under the terms of the MIT license.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
<strong> INFO: [Jan 17 10:43:55.736] loading configuration from file config_file=/etc/pelican/config.yml
</strong><strong> INFO: [Jan 17 10:43:55.745] configured wings with system timezone timezone=Australia/Melbourne
</strong><strong> INFO: [Jan 17 10:43:55.746] configured system user successfully gid=988 uid=988 username=pelican
</strong><strong> INFO: [Jan 17 10:43:55.748] fetching list of servers from API
</strong></code></pre>

{% hint style="danger" %}
Wait 10-15 minutes for Wings and the Panel to sync - Your node WILL show as is disconnected for this time.\
Go have a coffee, take the dog for a walk. Do something else for a bit.
{% endhint %}

### If the health is still red

Refer to [Pterodactyl / Pelican](/guides/troubleshooting/pterodactyl-pelican#wings-errors) and come back here once sorted

## Add Ports to the server

1. Log into the administrator panel with the administrator account
2. Click on Nodes
3. Click on the edit pen next to your node
4. Scroll down to allocations and click on the + symbol
   1. Play / Join subdomain

      |            |                                                                                                                                                                             |
      | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
      | IP Address | 0.0.0.0                                                                                                                                                                     |
      | Alias      | <p>the domain players will use to join your server. If defaults, play.yourdomain.com<br><br><em>This address:port will be shown to anyone with access to the panel</em></p> |
      | Port       | The port ranges you chose earlier, eg 6600-7700                                                                                                                             |
   2. Internal port range

      |            |                                                 |
      | ---------- | ----------------------------------------------- |
      | IP Address | 0.0.0.0                                         |
      | Alias      | Internal                                        |
      | Port       | The port ranges you chose earlier, eg 8600-8700 |
5. Click on Create
6.


# Crowdsec enrollment

## Register your Security Engines

{% hint style="info" %}
There will be an engine for each panel and node
{% endhint %}

1. navigate to <https://app.crowdsec.net/security-engines>
2. Your security engines will be listed to enroll, with their names displayed
3. Tick all of them, then select enrol

## Future reading

Please have a look at [Crowdsec](/guides/installation-guides/crowdsec) as it outlines adding blocklists, how to unblock IPs etc.


# Port forward

## Allow Ports through the Firewall

You will need to allow your SFTP and game server ports through the firewall

#### On Ubuntu, run the following commands

```
ufw allow <SFTPPort>
ufw allow <StartOfGamePortRange:EndOfGamePortRange>/tcp
ufw allow <StartOfGamePortRange:EndOfGamePortRange>/udp
ufw allow <StartOfInternalPortRange:EndOfInternalPortRange>/tcp
ufw allow <StartOfInternalPortRange:EndOfInternalPortRange>/udp
```

{% hint style="info" %}
Example:

```
ufw allow 2022
ufw allow 2500:2550/tcp
ufw allow 2500:2550/udp
ufw allow 
```

{% endhint %}

{% hint style="danger" %}
DO NOT PORT FORWARD THESE SERVICES:

* Adminer
  {% endhint %}

## Router / Modem

You will need to port forward your SFTP and game ports on your router / modem - you will need to reference Google for this one, sorry!


# First server

## **Import eggs**

1. Open the Panels admin ui
2. On the left, click on Eggs
3. Click on the new file arrow icon
4. The github link will have the Pelican / Pterodactyl eggs - review this list before importing any as they are a great starting point

### Third party eggs

{% hint style="info" %}
Pelican can use Pterodactyl eggs
{% endhint %}

You can download eggs from various places, but the official spots are best looked at first

* <https://github.com/mygameplatform/pelican-eggs>
* <https://pelican-eggs.github.io/pelican/>
* I have also modified some eggs, <https://github.com/trentnbauer/PelicanEggs>

#### Import third party eggs

1. Open the Panels admin ui
2. On the left, click on Eggs
3. Click on the new file arrow icon
   * If you have a url, click on URL and then paste the url to the egg
   * If you have the json or or yaml file, click on file and upload the egg

## Create a server

I haven't bothered to write this part of the documentation yet as it is relatively similar to Pterodactyl. Both of these pieces of software are well documented online

{% embed url="<https://youtu.be/tdEpa2bRfJk?t=945>" %}


# Databases

Some game servers or mods (like Minecraft plugins) can benefit from having a dedicated database. This can result in increased performance, and you can also connect multiple servers to the 1 database. A good use-case for this is the McMMO plugin. This allows your players levels to follow them between all of your Minecraft servers.

<img src="/files/4lBv3MKoAf99kxk3dB0K" alt="" class="gitbook-drawing">

## Database solutions

There are a few ways to do this, each with their own advantages and disadvantages

<table><thead><tr><th width="147"></th><th width="145" data-type="rating" data-max="5">Difficulty to configure</th><th width="132" data-type="rating" data-max="5">Difficulty to manage</th><th width="136" data-type="rating" data-max="5">Impact of cyber breach</th><th data-type="rating" data-max="5">Impact of database corruption</th></tr></thead><tbody><tr><td><strong>Shared Database</strong></td><td>2</td><td>1</td><td>3</td><td>3</td></tr><tr><td><strong>Per Node</strong></td><td>4</td><td>3</td><td>1</td><td>1</td></tr><tr><td><strong>Panel Database</strong></td><td>2</td><td>1</td><td>5</td><td>5</td></tr></tbody></table>

{% hint style="info" %}
Higher is worse (eg harder or less secure)
{% endhint %}

### Shared database

<img src="/files/dZNsgOPKZrJtWeGz0kMM" alt="" class="gitbook-drawing">

The database is hosted on a dedicated node, and the other nodes connect to this one. The other nodes need to be able to communicate with the host.&#x20;

**If you migrate a server from node  A to B everything will continue to function.**

**If your database corrupts you may lose all data for all nodes.**

If one of your game servers is breached and the bad actor got your database credentials they may be able to alter the other server DBs. This is unlikely though, as Pelican creates a dedicated user per database.

### Database per node

<img src="/files/5gb0t1jc1JFU5R3ueJ5x" alt="" class="gitbook-drawing">

This is the most secure method, as each node has its own database - there is no communication outbound. **Migrating a game server from node A to B will break the database connection**

**If your database corrupts you may lose all data on a single node**

If one of your game servers is breached and the bad actor got your database credentials they may be able to alter DBs on the node. This is unlikely though, as Pelican creates a dedicated user per database. **The bad actor will not be able to touch databases on other nodes.**

### Use the Panel database

<img src="/files/0TRxQCX6CfZeZCY4aZWz" alt="" class="gitbook-drawing">

***This is the most dangerous option.***&#x20;

If one of your game servers is breached and the bad actor gained database credentials, they may be able to alter your Pelican database. **This would allow them to create an administrator user and take over your Pelican install.**

**If your database corrupts you may lose all data for all nodes and the Panel data.** This would require a Pelican reinstall or restore from backup.

## Create your Database

This guide will step you through how to create a shared database from a Ptero / Pelican egg.

{% hint style="info" %}
This requires all nodes to be able to access the machine hosting the database. If your notes are not located all at the same site, you could use a VPN client or go down the per node route.
{% endhint %}

{% hint style="danger" %}
***Do not port forward your DB***
{% endhint %}

### Create your Database

If you haven't already, import the relevant database egg per [First server](/guides/installation-guides/pelican/first-server#import-eggs)

{% hint style="info" %}
**I am going to assume MariaDB for this guide - your commands may be different if using something else**

You may need to do some reading on your servers / mod / plugins database requirements to select the correct DB type
{% endhint %}

1. Create your database using one of your intenal ports

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><strong>Do not use one of your play / join ports</strong>. Doing so will expose your DB to the internet, which increases the risk of the db being breached or taken offline.</p></div>
2. Start the database container and ensure it is marked as running. You should see something similar to

   ```bash
   2026-01-25  9:42:34 0 [Note] Server socket created on IP: '0.0.0.0'.
   2026-01-25  9:42:34 0 [Warning] 'user' entry '@installer' ignored in --skip-name-resolve mode.
   2026-01-25  9:42:34 0 [Warning] 'proxies_priv' entry '@% container@installer' ignored in --skip-name-resolve mode.
   2026-01-25  9:42:34 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
   2026-01-25  9:42:34 0 [Note] /usr/sbin/mariadbd: ready for connections.
   Version: '11.5.2-MariaDB-ubu2404'  socket: '/home/container/run/mariadbd/mariadbd.sock'  port: 7501  mariadb.org binary distribution
   Welcome to the MariaDB monitor.  Commands end with ; or \g.
   Your MariaDB connection id is 3
   Server version: 11.5.2-MariaDB-ubu2404 mariadb.org binary distribution

   Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

   Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
   ```

#### Add database to Pelican Panel

Now we need to add the database to the panel, assign a host and create the Database user

1. Log into your panel and navigate to the Admin UI
2. On the left, click on Database hosts
3. Click on the + symbol
4. Preperations tab:
   1. Save the provided credentails to your password manager
   2. Click next / OK
5. Database setup tab:
   1. Copy paste the commands from the Panel into the MariaDB console
   2. Run the following commands to allow for login outside of local host (127.0.0.1)

      ```bash
      RENAME USER 'pelicanuser'@'127.0.0.1' TO 'pelicanuser'@'%';
      FLUSH PRIVILEGES;
      ```
   3. Click on next / OK
6. Panel setup tab:

   <table><thead><tr><th width="242"></th><th></th></tr></thead><tbody><tr><td>Host</td><td>DNS name or IP of the wings node running the database container</td></tr><tr><td>Port</td><td>The port selected during creation</td></tr><tr><td>Display name</td><td>Whatever you want - I would suggest the hostname / DNS name of the server</td></tr><tr><td>Linked nodes</td><td>Set this to any Wings nodes you want to talk to this DB.<br>These nodes need to be able to reach the hostname / IP in the host field</td></tr></tbody></table>

   1. Click on create

#### Configure backup schedules

Backups are good, use backups.

Feel free to import one of my existing schedules from <https://github.com/trentnbauer/PelicanEggs/tree/main/schedules>

### Per node

Follow the above guide, but you will need to do it for each node. In step 6, set the linked node as the machine running the database - do not set any other linked nodes.

### Panel database

I will not show you how to configure this. The risk to bricking your install of Pelican is too high. **Follow the shared guide**

***

## Troubleshooting

#### "Unable to resolve host" or similar message.

Try your servers IP address - some containers are unable to resolve internal network names.


# Optional: Adding URLs to Dockflare

There are a few way to add additional addresses to Dockflare, but the easiest is

1. Navigate to your Dockflare url (by default dockfare.yourdomain.com)
2. Log in with your Cloudflare Zero Trust credentials
3. Click on 'add manual rule' and fill out the form

### Examples

Below are some examples with specific use bases

#### Public DynMap

Lets pretend that we want DynMap hosted in port 3333 to be available publicly at maps.yourdomain.com. Anyone who browses to this address will be shown the map

<table><thead><tr><th width="223">Data</th><th>Example</th></tr></thead><tbody><tr><td>Public Hostname</td><td>maps.yourdomain.com</td></tr><tr><td>Service</td><td>http://yourservername:3333</td></tr><tr><td>Access Policy</td><td>Bypass</td></tr></tbody></table>

#### Administrator UI

Lets pretend that we want an administrator UI hosted in port 1234 to be available to specific users at admin.yourdomain.com. We do not want this UI available to the general public.

<table><thead><tr><th width="223">Data</th><th>Example</th></tr></thead><tbody><tr><td>Public Hostname</td><td>admin.yourdomain.com</td></tr><tr><td>Service</td><td>http://yourservername:1234</td></tr><tr><td>Access Policy</td><td><ul><li>*.TLD or</li><li>configure an Access Group policy under the settings tab and apply that</li></ul></td></tr></tbody></table>


# Optional: Adminer proxy

{% hint style="info" %}
You can access Adminer locally at the relevant port - this will most likely be enough and you don't need to expose it. If you are hosting on a VPS or do not have LAN access to the machine, you will need to set up the proxy to use it.
{% endhint %}

## If you do need to expose it,

Add the following lines to your .env file and restart your pull your docker compose config

```
ADMINER_PROXY=true
ADMINER_SUBDOMAIN=db
ADMINER_POLICY=default_tld
```

Your Adminer instance will be available at db.yourdomain.com.&#x20;

Deleting the `ADMINER_PROXY=true` variable from your env and pulling your compose will remove the proxy.

{% hint style="danger" %}
Please test and ensure that the link is secure, as you may exposing your database, without authentication, to the internet
{% endhint %}


# Crowdsec

[CrowdSec](https://www.google.com.au/search?q=CrowdSec\&safe=active\&ssui=on\&ved=2ahUKEwirpr2p7aCSAxXAcWwGHVrCIgUQgK4QegQIARAB) is an open-source, community-driven Intrusion Prevention System (IPS) that analyzes server logs to detect and block malicious IP addresses in real time. It acts like a modern, collaborative [Fail2Ban](https://www.google.com.au/url?sa=i\&source=web\&rct=j\&url=https://wz-it.com/en/blog/explained-and-set-up-crowdsec/\&ved=2ahUKEwirpr2p7aCSAxXAcWwGHVrCIgUQy_kOegQIARAD\&opi=89978449\&cd\&psig=AOvVaw0A0EcHScJxDp_VfPoMJywX\&ust=1769229955543000), sharing threat intelligence among users to create a collective, constantly updated, and curated blocklist.&#x20;

<div data-full-width="false"><figure><img src="https://docs.crowdsec.net/img/simplified_SE_overview.svg" alt=""><figcaption><p>Stolen from Crowdsec's website</p></figcaption></figure></div>

<table><thead><tr><th width="217.20001220703125"></th><th></th></tr></thead><tbody><tr><td>Crowdsec Engine</td><td>The crowdsec engine is the processor and central logging unit that watches your log files and what is occuring on your server. It tells the bouncer what to do<br>The engine also reports your data back to the Crowdsec UI to manage there if you enable this</td></tr><tr><td>Crowdsec Bouncer</td><td>The bouncer takes action, eg blocking an IP, and requires a connection to the engine to function.</td></tr></tbody></table>

## Before following these guides

All of my Crowdsec guides expect the following:

* [ ] Docker installed
* [ ] Ubuntu or similar OS
* [ ] An existing Crowdsec account
* [ ] You have an enrollment key ready to go [#generate-your-enrollmemt-key](#generate-your-enrollmemt-key "mention") and
* [ ] **You have read this page in its entirety**

## Generate your Enrollmemt key

All of my compose stacks have variables set to enrol your engine into Crowdsec.

1. Navigate to <https://app.crowdsec.net/>
2. Click on 'Enroll' and copy the enrolment key to clipboard

   <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>This key can be saved to your password vault as it rarely changes</p></div>

## Compose stacks

All of my Crowdsec stacks will include an 'init' (initialization) container, which is used to download the configuration files required. **The configuration file URLs are readable in the compose file and** [**publicly available on my Github**](https://github.com/trentnbauer/HomelabPublic/tree/main/crowdsec)**.** These are my production configs and may change over time. As such, I would recommend hosting your own.

Restarting the stack may re-run the init container. If you have made changes to the config file, these may be overwritten.

#### Here is an example 'init' container,

<pre class="language-yaml" data-overflow="wrap" data-line-numbers><code class="lang-yaml">services:
    init-crowdsec:
    image: alpine:latest
    volumes:
      - crowdsec:/etc/crowdsec
    command: >
      sh -c "apk add --no-cache wget ca-certificates &#x26;&#x26;
             mkdir -p /etc/crowdsec/acquisitions.d &#x26;&#x26;
<strong>             wget -qO /etc/crowdsec/acquis.yaml https://raw.githubusercontent.com/trentnbauer/HomelabPublic/main/crowdsec/unifi.yaml"
</strong></code></pre>

{% hint style="info" %}
I have highlighted the line downloading the configuration file. You can browse to the address to view the contents.
{% endhint %}

***

## Using Crowdsec

### Where should I deploy my Crowdsec Engine/s

Prioritize protecting anything that is

* A bridge between your local network and the internet (e.g. your firewall)
* Port forwarded
* High impact if brought offline

{% hint style="info" %}
While I recommend protecting as much as you can, each engine increases your risk of being rate limited. Because of this, I would recommend protecting your public facing services first.

\
Applications behind a Cloudflare tunnel are not included in this as the entry point is the Cloudflare network. Refer to [Cloudflare security rules](/guides/installation-guides/crowdsec/cloudflare-security-rules) for protecting your tunnels.
{% endhint %}

### Add Blocklists

Crowdsource lists are public lists of malicious IP addresses. You can subscribe to a list to automatigically add those to the Cloudflare [Cloudflare](/guides/installation-guides/pelican/cloudflare#security-rules) generated by Crowdsec

{% hint style="info" %}
Free tier Crowdsec has a limit on how many blocklists you can subscribe to
{% endhint %}

1. Navigate to <https://app.crowdsec.net/blocklists/> and review the lists. **Most are paid**
2. Open a list that sounds good
3. Click on subscribe
4. Select either organization, or engine (and select your Pelican engine)
5. For remediation, select Captcha
6. Click on OK / Save / Subscribe

### I personally use the below lists

{% embed url="<https://app.crowdsec.net/blocklists/65a56c520469607d9badb817>" %}

{% embed url="<https://app.crowdsec.net/blocklists/65a56c010469607d9badb80f>" %}

{% embed url="<https://app.crowdsec.net/blocklists/65a55718ff8363f6556e9d4b>" %}

### Decisions

Once enrolled, you will be able to see your engines decisions (blocked IPs) at <https://app.crowdsec.net/decisions> alongside some other useful information.&#x20;

{% hint style="info" %}
If this is a fresh install of Crowdsec, you likely won't have any 'decisions' listed
{% endhint %}

Here is an example from my instance;

<figure><img src="/files/MrM9ujRHbwCdpfls39Rm" alt=""><figcaption><p>This malicious IP from Turkmenistan is "very noisey", which means it shows up in a lot of other peoples Crowdsec instances.</p></figcaption></figure>

#### **Unblock an IP**

If you need to delete a false positive or unblock an IP,

1. Navigate to <https://app.crowdsec.net/decisions>&#x20;
2. Click on the bin icon next to the IP address
3. Wait a few minutes for things to sync

or

1. Exec into the relevant engine
2. Run the command `cscli decisions delete --ip IPADDRESS`&#x20;
3. Wait a minute or 2 for Crowdsec to talk to the bouncer, to talk to the service

***

#### Crowdsec is best deployed using a central engine, [per this documentation.](https://docs.crowdsec.net/u/user_guides/multiserver_setup/)

**My guides aren't written this way.** I'm using multiple engines to make it easier to spin up and down stacks. There are some benefits / negatives;

* **Easier to deploy**\
  My stacks are written to be spin-up-and-go. You will only need to accept the enroll request in the Crowdsec UI
* **Risk of being rate limited**\
  Due to having multiple engines, you run the risk of being rate limited by Crowdsec. Each engine increases your API calls.
* **Syncing across engines**\
  Using a central engine will instantly block a malicious IP across all your bouncers. This may not occur using multiple engines.

*If you want to use a single engine, you're best to look at other guides.*


# UniFi

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Total Time Required</td><td>20 minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>Crowdsec, UniFi</td></tr></tbody></table>

<img src="/files/ey3i8si6v8x957kjAto9" alt="" class="gitbook-drawing">

## Prerequisites

* Crowdsec account
* Your UniFi Firewall is on [the supported list](https://app.crowdsec.net/hub/author/Teifun2/remediation-components/cs-unifi-bouncer) (may work with unsupported)
* [Your Crowdsec enrollment key](/guides/installation-guides/crowdsec#generate-your-enrol-key)

#### This stack monitors...

* Syslog data from UniFi controller
* Crowdsec blocklists (if subscribed)
* [Additional blocklists that are normally paid](https://github.com/wolffcatskyy/crowdsec-blocklist-import?tab=readme-ov-file#included-blocklists)
* Crowdsec whitelists

#### ... And changes

* UniFi firewall

## UniFi changes

### Service account

You will need to generate a service account for the UniFi bouncer to log into and use

1. Navigate to <https://unifi/network/default/admins/>
2. Untick "Admin Permissions"
3. Create a new user and fill out the below

   |                          |                       |
   | ------------------------ | --------------------- |
   | First name               | Crowdsec              |
   | Last name                | Bouncer               |
   | Admin                    | True                  |
   | Restrict to local access | True                  |
   | Username                 | \<randomly generated> |
   | Password                 | \<randomly generated> |
   | Use a predifined role    | False                 |
   | Unifi                    | Full management       |
   | OTHER ROLES              | None                  |
4. Save your username and password to your text editor
5. Click on Create

{% hint style="info" %}
We are randomly generating the username and password as this account has full write access (although only in LAN) to your UniFi controller - you do not want to use simple credentials for this level of access
{% endhint %}

### Forward Logs

1. Navigate to <https://unifi/network/default/settings/cybersecure/traffic-logging>
2. Set Syslog to SIEM server or external
3. Set the server address to the IP of the machine that will run this stack
4. Click on Apply Changes

### Enable Firewall zones

1. Navigate to <https://unifi/network/default/settings/zones>
2. Enable Firewall zones&#x20;

{% hint style="warning" %}
this will import your existing firewall rules and port forwards to the new Firewall zones, but please test and ensure your critical infrastructure is still working
{% endhint %}

## Deploy Compose stack

Fill out the below env file and deploy your stack

```dotenv
# --- CrowdSec Configuration ---
CROWDSEC_ENROLL_KEY=
MAX_DECISIONS=4000
BOUNCER_KEY=MandatoryUnsolvedSnippetMonetizeDrizzle8
BLOCKLISTPASSWORD=OccupantCorporalVividness9DiabetesCrumb

# --- Syslog Configuration ---
# The port your UniFi equipment will send logs to
SYSLOG_PORT=514
TZ=UTC

# --- UniFi Controller Configuration ---
# The IP or Hostname of your UniFi Controller (UDM/Gateway)
UNIFI_HOST=https://unifi
SKIP_TLS_VERIFY=true
UNIFI_USER=
UNIFI_PASS=
```

{% hint style="warning" %}
If port 514 is not available on your host, you will need to update the port at [#forward-logs](#forward-logs "mention") to whatever port you select.
{% endhint %}

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/crowdsec-unifi.yml>" %}

### Host Firewall

1. SSH into your host
2. Allow your syslog port through the firewall (by default 514)

{% hint style="info" %}
If you need to change your syslog port, don't forget to update UniFi at [#forward-logs](#forward-logs "mention") and your .env file   [#deploy-compose-stack](#deploy-compose-stack "mention")
{% endhint %}

### Register the blocklist container

1. Exec into the Crowdsec engine container
2. Run the below command

   ```bash
   cscli machines add blocklist --password $BLOCKLISTPASSWORD -f /etc/crowdsec/blocklist_credentials.yaml --force
   ```
3. Restart the engine and blocklist containers
4. Check the engine container logs, you should see something similar to the below

   <pre class="language-bash" data-overflow="wrap"><code class="lang-bash">time="2026-04-25T12:43:59+10:00" level=info msg="(blocklist/blocklist-import) external/blocklist (AbuseIPDB) for 1000/1000 decisions : 24h ban on Ip 137.184.107.118" module=db
   time="2026-04-25T12:43:59+10:00" level=info msg="(blocklist/blocklist-import) external/blocklist (AbuseIPDB) for 999/1000 decisions : 24h ban on Ip 137.184.105.225" module=db
   time="2026-04-25T12:43:59+10:00" level=info msg="(blocklist/blocklist-import) external/blocklist (AbuseIPDB) for 998/1000 decisions : 24h ban on Ip 137.184.76.231" module=db
   time="2026-04-25T12:43:59+10:00" level=info msg="(blocklist/blocklist-import) external/blocklist (AbuseIPDB) for 997/1000 decisions : 24h ban on Ip 137.184.76.120" module=db
   time="2026-04-25T12:43:59+10:00" level=info msg="(blocklist/blocklist-import) external/blocklist (AbuseIPDB) for 996/1000 decisions : 24h ban on Ip 137.184.64.22" module=db
   </code></pre>

{% hint style="info" %}
This allows the blocklist container to authenticate to the Crowdsec engine. You may need to restart the blocklist and crowdsec engine containers after this
{% endhint %}

### Confirm the bouncer has logged into the account

1. Navigate to <https://unifi/network/default/admins/>
2. Review the list and find Crowdsec Bouncer - the last activity should state "now"

If not, review the bouncer and engine container logs

## Enroll the engine

1. Navigate to <https://app.crowdsec.net/security-engines>
2. Locate the UniFi in the enrollment list
3. Enrol the engine
4. Wait 5 minutes

## Check UniFi firewall rules exit

1. Navigate to <https://unifi/network/default/settings/zones>
2. You will have a stack of "cs-unifi-bouncer" rules

***

## Known Issues

### UniFi network appliance crashing

This will show as

* Slowness accessing UniFi network software
* Alerts for router being offline
* Router showing as offline in at&#x20;

[There is a known bug where too many blocked IPs crashes the UniFi network application](https://github.com/wolffcatskyy/crowdsec-blocklist-import?tab=readme-ov-file#firewall-bouncer-limits--ipset-sizing)

#### You will first need to remove the firewall rules and lists

This is a bit of a nightmare as the network application will crash often while you are doing this.

1. Stop the Crowdsec UniFi stack
2. Browse to <https://unifi/network/default/settings/policy-table>
3. Click on Manage, and tick all of the `CS-` rules
4. Click on Delete and then proceed
5. Browse to <https://unifi/network/default/settings/networks> and scroll down to lists
6. Click on Manage, and tick all of the `CS-` rules
7. Click on Delete and then proceed

**Or follow this:** [**https://github.com/wolffcatskyy/crowdsec-blocklist-import?tab=readme-ov-file#recovery-network-app-crash**](https://github.com/wolffcatskyy/crowdsec-blocklist-import?tab=readme-ov-file#recovery-network-app-crash)

#### Adjust the blocklist limit

1. Update the compose env file variable `MAX_DECISION` - you will need to reduce the number
2. Restart the stack and see how you go

{% hint style="info" %}
The env file is set to 4000, which I believe is much lower than the cap - IF you have issues, I would suggest halving this and seeing how it goes. You can slowly increase it once its happy.
{% endhint %}


# Ubuntu

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Total Time Required</td><td>10 minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>Crowdsec, Docker Compose</td></tr></tbody></table>

<img src="/files/QaIVMXjEOuGwjES7gtXt" alt="" class="gitbook-drawing">

## Prerequisites

* Crowdsec account
* [You have subscribed to atleast 1 block list](/guides/installation-guides/crowdsec#add-blocklists)
* [Your Crowdsec enrollment key](/guides/installation-guides/crowdsec#generate-your-enrol-key)

#### This stack monitors...

* SSH logs
* Sudo logs
* Crowdsec blocklists
* Crowdsec whitelists

#### ... And changes

* Host firewall

## Docker Compose

```dotenv
# --- CrowdSec Engine Settings ---
CROWDSEC_ENROLL_KEY=
BOUNCER_KEY=
CROWDSEC_NAME=
CROWDSEC_PORT=8080
```

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/crowdsec-firewall.yml>" %}


# Proxmox

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Total Time Required</td><td>15 minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>Crowdsec, Docker Compose</td></tr></tbody></table>

<img src="/files/Wt0dMIOVIApAhM4yPP29" alt="" class="gitbook-drawing">

## Prerequisites

* Crowdsec account
* [You have subscribed to atleast 1 block list](/guides/installation-guides/crowdsec#add-blocklists)
* [Your Crowdsec enrollment key](/guides/installation-guides/crowdsec#generate-your-enrol-key)

#### This stack monitors...

* SSH logs
* Sudo logs
* Proxmox logs
* Crowdsec blocklists
* Crowdsec whitelists

#### ... And changes

* Host firewall

{% hint style="warning" %}
Blocked IPs do not show in the Proxmox UI
{% endhint %}

## Docker Compose

```dotenv
# --- CrowdSec Engine Settings ---
CROWDSEC_ENROLL_KEY=
BOUNCER_KEY=
CROWDSEC_NAME=
CROWDSEC_PORT=8080

# --- Proxmox API key details ---
PVE_TOKEN_ID=
PVE_TOKEN_SECRET=
```

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/crowdsec-pve.yml>" %}


# Synology NAS


# Cloudflare security rules

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Total Time Required</td><td>15 minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>Crowdsec, Cloudflare</td></tr></tbody></table>

<img src="/files/xtg9NP02DwIKyaN1xEk0" alt="" class="gitbook-drawing">

## Prerequisites

* Crowdsec account
* [You have subscribed to atleast 1 block list](/guides/installation-guides/crowdsec#add-blocklists)
* [Your Crowdsec enrollment key](/guides/installation-guides/crowdsec#generate-your-enrol-key)

{% hint style="warning" %}
I recommend setting up a unique stack per domain you manage. This reduces the impact if the API key is leaked.
{% endhint %}

#### This stack monitors...

* Crowdsec blocklists

#### ... And changes

* Cloudflare security rules

## Cloudflare

Generate your API key, get your details and create your security rules

### Get your Account ID

1. Browse to <https://dash.cloudflare.com/>
2. Next to your name, click on the 3 dots and select Copy Account ID
3. Save to your notepad, `CF_ACCOUNTID=`

### Get your Zone ID

1. Browse to <https://dash.cloudflare.com/?to=/:account/home/domains>&#x20;
2. Click manage next to your domain
3. Scroll down and locate "API" on the right
4. Save  your Zone ID to your notepad, `CF_ZONE_ID=`

### Generate an API key

1. Navigate here <https://dash.cloudflare.com/profile/api-tokens>
2. Click on Create token > custom token
   1. Give your token a name and fill out the below permissions

      <table><thead><tr><th width="148"></th><th width="357"></th><th></th></tr></thead><tbody><tr><td>Account</td><td>Account Filter Lists</td><td>Edit</td></tr><tr><td>Account</td><td>Firewall Access Rules</td><td>Edit</td></tr><tr><td>Zone</td><td>Zone</td><td>Read</td></tr><tr><td>Zone</td><td>Firewall Services</td><td>Edit</td></tr></tbody></table>
   2. Account resources

      | Field   | Data         |
      | ------- | ------------ |
      | Include | All Accounts |
   3. Zone resources

      |         |               |             |
      | ------- | ------------- | ----------- |
      | Include | Specific Zone | Your Domain |
   4. Click on continue to summary
3. Save your API key to your notepad, `CF_APITOKEN=`

### Set your Security rules

Configure some security rules to reduce the risk of malicious actors accessing your domain

1. Navigate to <https://dash.cloudflare.com/>
2. Select your domain
3. On the left, click expand Security and select rules
4. Click on create rule > custom rule
   1. Next to Expression Preview, click on 'edit expression' to get the free text field
5. Create a rule for each of the below

### Block bots

This policy will show a Captcha challenge to any IPs suspected of botting

<table><thead><tr><th width="229">Field</th><th>Data</th></tr></thead><tbody><tr><td>Rule Name</td><td>Block Bots</td></tr><tr><td>Expression</td><td>(cf.client.bot)</td></tr><tr><td>Choose action</td><td>Managed Challenge</td></tr><tr><td>Place at</td><td>First</td></tr></tbody></table>

### Challenge Threat Score

These IPs are potentially malicious. These addresses will be prompted for Captcha

<table><thead><tr><th width="229">Field</th><th>Data</th></tr></thead><tbody><tr><td>Rule Name</td><td>Challenge Threat Score</td></tr><tr><td>Expression</td><td>(cf.threat_score gt 10)</td></tr><tr><td>Choose action</td><td>Managed Challenge</td></tr><tr><td>Place at</td><td>Custom - after 'Block Bots'</td></tr></tbody></table>

### Block Threat Score

These IPs are very likely to be malicious. These addresses will be blocked

<table><thead><tr><th width="229">Field</th><th>Data</th></tr></thead><tbody><tr><td>Rule Name</td><td>Challenge Threat Score</td></tr><tr><td>Expression</td><td>(cf.threat_score gt 50)</td></tr><tr><td>Choose action</td><td>Block</td></tr><tr><td>Place at</td><td>Custom - after 'Challenge Threat Score'</td></tr></tbody></table>

{% hint style="info" %}
An additional rule will be created by the Crowdsec CF Bouncer container after the Compose file is ran
{% endhint %}

## Docker Compose

Fill out the below env using your notes

```dotenv
# --- CrowdSec Engine Settings ---
CROWDSEC_ENROLL_KEY=

# --- Cloudflare Credentials ---
CF_APITOKEN=
CF_ACCOUNTID=
CF_ZONE_ID=
```

{% @github-files/github-code-block url="<https://github.com/trentnbauer/HomelabPublic/blob/main/docker-compose/crowdsec-cloudflare.yml>" %}


# OLD

## Secure a Server

### Install Container

### Configure Container

#### Firewall Bouncer

This guide is applicable to Ubuntu 20.04 LTS, which uses nf tables by default

1. Run the below commands and take note of the API key

   ```bash
   apt install crowdsec-firewall-bouncer-iptables -y
   cscli bouncer add firewall #copy the API key
   ```
2. Remove and edit the config file

   ```bash
   rm /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
   nano /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
   ```
3. Paste in my config below (please update the variables marked with a $)

   <pre class="language-yaml"><code class="lang-yaml">mode: nftables
   <strong>update_frequency: 300s
   </strong>log_mode: file
   log_dir: /var/log/
   log_level: info
   log_compression: true
   log_max_size: 100
   log_max_backups: 3
   log_max_age: 30
   api_url: http://127.0.0.1:8080/
   api_key: $APIKeyFromStep1
   insecure_skip_verify: false
   disable_ipv6: false
   deny_action: DROP
   deny_log: false
   supported_decisions_types:
     - ban
   #to change log prefix
   #deny_log_prefix: "crowdsec: "
   #to change the blacklists name
   blacklists_ipv4: crowdsec-blacklists
   blacklists_ipv6: crowdsec6-blacklists
   #type of ipset to use
   ipset_type: nethash
   #if present, insert rule in those chains
   iptables_chains:
     - INPUT
   #  - FORWARD
   #  - DOCKER-USER

   ## nftables
   nftables:
     ipv4:
       enabled: true
       set-only: false
       table: crowdsec
       chain: crowdsec-chain
       priority: -10
     ipv6:
       enabled: true
       set-only: false
       table: crowdsec6
       chain: crowdsec6-chain
       priority: -10

   nftables_hooks:
     - input
     - forward

   # packet filter
   pf:
     # an empty string disables the anchor
     anchor_name: ""

   prometheus:
     enabled: false
     listen_addr: 127.0.0.1
     listen_port: 60601
   </code></pre>
4. Restart the service and review the nft tables

   ```bash
   systemctl restart crowdsec-firewall-bouncer.service
   systemctl restart crowdsec
   nft list tables
   ```
5. You should see an output similar to below

   ```bash
   table ip nat
   table ip filter
   table ip6 filter
   table ip crowdsec
   table ip6 crowdsec6
   ```

## Secure a Domain

### Cloudflare Bouncer

I've read online that the original bouncer isn't as good as the workers module but I'm a bit nervous to use the worker as, to me, it reads like it reviews every request coming through Cloudflare. This is great because it checks current data against current data but you only get x amount of free worker compute. If you're DDOS'd I imagine the bill would be big. So I've gone with the WAF based bouncer, which is pretty slow to update and can get API limited.

### Generate an API key for Cloudflare

1. Navigate to[ user profile > API keys](https://dash.cloudflare.com/profile/api-tokens)
2. Create a new API token and pick custom token
3. Give it the following permissions\\

   <figure><img src="/files/uNmOt5Va7vR2uailUKNf" alt=""><figcaption><p>You can limit the API key to certain domains here but you can also do it in the Crowdsec config file</p></figcaption></figure>
4. Click continue to summary and then create token
5. Copy the API token into your password vault

#### Gather your Account and Zone IDs

1. Go to your [Cloudflare dashboard](https://dash.cloudflare.com/) and open one of the domains you wish to protect with Cloudflare
2. Scroll down and locate the API section on the right
3. Take note of your Zone ID (I would recommend formatting it like $ZONEID #my.domain.com)
4. Take note of your Account ID
5. Repeat steps 1 - 3 for each domain you wish to protect (the account ID will most likely be the same for each)

#### Install the Crowdsec module and bouncer

Example config:

{% code overflow="wrap" %}

```yaml
#Cloudflare Config.
cloudflare_config:
  accounts:
  - id: ACCOUNT_ID
    token: API_TOKEN
    ip_list_prefix: crowdsec
    default_action: managed_challenge
    zones:
    - actions:
      - managed_challenge # valid choices are either of managed_challenge, js_>      zone_id: 182bacc7aaeda7bf1f1e35c79883b3f8 #agamersgrind.com
    - actions:
      - managed_challenge
      zone_id: ZONE_ID1 #my.domain.com
    - actions:
      - managed_challenge
      zone_id: ZONE_ID2 #your.domain.net
      ### Want to add another zone? Copy paste from here...
    - actions:
      - managed_challenge
      zone_id: ZONE_ID3 #grandmas.domain.xyz
      ### ... End copy paste here

  update_frequency: 5m # the frequency to update the cloudflare IP list. I have set this frequency to be very low to reduce risk of API limiting

```

{% endcode %}

## Allow CURL or remote access to Crowdsec Engine

Some tools, such as Docker containers, are considered 'external' to the host device. This causes Crowdsec (and potentially the firewall) to block communications for this app or module.

{% hint style="info" %}
I had to do this for the Wordpress Crowdsec plugin, which sits inside my Wordpress container.
{% endhint %}

1. SSH into the server you want to send your data to
2. Input the below command allow Crowdsec through the firewall and to edit the Crowdsec config file

   ```bash
   ufw allow 8080
   nano /etc/crowdsec/config.yml
   ```
3. Locate `127.0.0.1:8080` and change it to *0.0.0.0:8080*\
   This allows any device to talk to the Crowdsec engine on that device
4. Save the config file
5. Input the below command to restart Crowdsec

   ```bash
   systemctl restart crowdsec
   ```
6. You will now be able to CURL the Crowdsec engine


# TEMPLATE

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Total Time Required</td><td>15 minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>Crowdsec, Cloudflare</td></tr></tbody></table>

<img src="/files/5bbbboO81YPjePUAx6z8" alt="" class="gitbook-drawing">

## Prerequisites

* Crowdsec account
* [You have subscribed to atleast 1 block list](/guides/installation-guides/crowdsec#add-blocklists)
* [Your Crowdsec enrollment key](/guides/installation-guides/crowdsec#generate-your-enrol-key)

## Docker Compose

```dotenv
# --- CrowdSec Engine Settings ---
CROWDSEC_ENROLL_KEY=
BOUNCER_KEY=

# --- Cloudflare Credentials ---
CF_APITOKEN=
CF_ACCOUNTID=
CF_ZONE_ID=
```


# Ansible

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>1 Hour</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>SSH, Docker Compose, YAML</td></tr></tbody></table>


# Portainer


# Restore stack env file

Sometimes we accidentally remove items from our environmental variables section and need to restore them.

#### You will need backups BEFORE following this

If you do not have backups, you may be out of luck!

For backup software, I personally use [Borg UI](https://github.com/karanhudia/borg-ui) for file level backups and PBS for entire VMs - I may write a guide for it in the future, but the software is constantly changing at the moment.&#x20;

[Veeam B\&R is free for 7 machines in a homelab](https://www.veeam.com/blog/backup-replication-community-edition-features-description.html) which is a great product to have on your resume.

## Get the stack ID

1. Log into your Portainer instance and navigate to the relevant stack
2. Have a look at the URL - you are looking for the ID - I have highlighted in bold below\
   `http://portainer.yourdomain.com/#!/2/docker/stacks/crowdsec-unifi?`**`id=867`**`&type=2&regular=true&orphaned=false&orphanedRunning=false`

## Get the volume path

1. Log into your Portainer instance
   1. If you have multiple hosts, select the host running Portainer
2. Select Containers
3. Locate Portainer in the list and open it
4. Scroll down to Volumes and locate the /data volume
5. If it is direct mount (host/path is grey), copy the path\
   Skip the rest of this section
6. If it is a docker volume (host/path is a link), click on the volume name
7. Copy the mount path

## File to restore

The file to restore will be your mount path will be **`MOUNTPATH`**`/compose/`**`STACKID`**`/stack.env`&#x20;

#### Some examples,

```
var/snap/docker/common/var-lib-docker/volumes/portainer/_data/compose/867/stack.env
var/lib/docker/volumes/portainer/_data/compose/123/stack.env
etc/portainer/compose/666/stack.env
```


# Proxmox


# Increase size of VM disk

<table data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>Time Required</td><td>15 Minutes</td></tr><tr><td>Difficulty</td><td>Easy</td></tr><tr><td>Required Knowledge</td><td>Proxmox, machine boot order</td></tr></tbody></table>

## Prerequisites

{% columns %}
{% column %}

#### Requirements

* Administrator access to PVE webUI
* GParted ISO in your ISO store
  {% endcolumn %}

{% column %}

#### Recommended

{% endcolumn %}
{% endcolumns %}

### Increase size of disk

1. Log into PVE
2. Locate the VM and shut it down
3. Select the hardware tab and select the disk
4. Click on disk action > resize
5. Input how many GB's you want to increase it by

### Boot into GParted

1. Add a new CD drive
2. Select the GParted ISO
3. Power on the VM, interrupt its boot and select CD drive
4. Select the default options as GParted starts up
5. Open the tool on the desktop and you will see something similar to below,\
   ![](/files/UY6IL2NTK9LuYQTjVllN)![](/files/BNTVMA7n22f65VO5GbND)

Take note of the order of the partitions. The screenshot on the left shows the SDA next to unallocated. We can increase SDA without any issues

The screenshot on the right shows that there is SDA2 in between SDA1 and the unallocated space. We are assuming we need to increase SDA1, as it is the larger of the partitions and is likely the partition we want to increase.

<details>

<summary>There is a partition between the partition I am increasing and the unallocated space</summary>

1. Select the partition in between (example photo shows SDA2)
2. Click on edit and you will see something similar to below\
   ![](/files/fVpA3iEmSU19fhfWFCBv)
3. Click the middle of the white block and drag it all the way to the right
4. Click on resize / move
5. If you do not get any errors, continue

</details>

### Increase the partition

1. Click on edit on the partition you want to increase. You will see something similar to below\
   ![](/files/aqIR94lKhisfYCYN87JB)
2. Click on the right most arrow and drag it as far right as it will go
3. Click on resize / move
4. Click on the green tick and wait
5. Shutdown the VM

### Remove the CD drive

1. Log into PVE
2. Locate the VM and click on Hardware
3. Select the CD drive, then click on remove
4. Power on the VM




---

[Next Page](/llms-full.txt/1)

