[{"data":1,"prerenderedAt":369},["ShallowReactive",2],{"search-api":3},[4,11,19,29,37,45,54,62,73,82,92,100,110,119,129,139,151,160,168,175,182,189,196,206,215,223,231,238,246,254,263,270,277,288,295,301,308,314,320,326,333,339,345,351,360],{"id":5,"path":6,"dir":7,"title":8,"description":7,"keywords":9,"body":10},"content:0.index.md","/","","Home",[],"     Welcome to docu  · djeex   Docs, more docs. Tips and experiments. Build your homelab and your own NAS.         git.djeex.fr    Check my nonsense projects",{"id":12,"path":13,"dir":14,"title":15,"description":7,"keywords":16,"body":18},"content:1.about:1.welcome.md","/about/welcome","about","Welcome",[17],"About the documentation","   docu  · what?   Docu  · djeex  is a site containing the documentation of my personal servers, originally created to easily keep track of my configurations and commands.  \nMy infrastructure is built around the Debian 13 + Docker combo, making exporting and deployment simpler.  \nSpecial thanks to   Nipah ,   Xenio , and others for their patience and support. Most of this content comes directly from them.  About the documentation  The documentation provided here is experimental and shared in a spirit of open knowledge and experience.  \nIt is not intended to build production-grade or industrialized infrastructure.  \nIt may contain mistakes and/or approximations.  Naturally, this documentation should only be used within a strictly legal framework.    Available or Upcoming Documentation      Serveex    Step-by-step Homelab Deployment Guide    Stockeex    (coming soon)  Build your own home NAS to store your data and media",{"id":20,"path":21,"dir":22,"title":23,"description":7,"keywords":24,"body":28},"content:2.general:1.networking:1.nat.md","/general/networking/nat","networking","NAT & DHCP",[25,26,27],"What is a \"port\"?","Port Forwarding","DHCP","   Router and NAT   🎯    Goals:   Understand how port forwarding works  Learn how to configure router NAT  Learn how to issue DHCP leases (fixed IPs)    What is a \"port\"?   Ports are different channels through which your router sends and receives data. This allows multiple services to run simultaneously.  \nWhen it receives data through a port, your router forwards that data to the machine that:   either initiated the request,  or is configured to receive data on a specific port.  Your router has over 65,000 ports available.  Some programs and applications are designed to use specific ports. For example, when your network sends data from an HTML page, the router receives it through port 80 (non-secure) or port   443  (secure via SSL).  So, your router acts as a data dispatcher between the internet and your local machines.  Port Forwarding   Forwarding a   port  means setting a rule that specifies which   source  can send data to which   port  on your router, which will then forward it to a specific   port  on a specific   machine . The   sources  and   destination machine  are identified by their IP addresses.     Variable  Description  Example     source machine  IP of the source machine (from the internet)   All   123.45.67.89    source port  Incoming port on the router   443    destination port  Port on the destination machine   3000    destination machine  IP of the target machine (on your local network)   192.168.1.50  According to the table:  \nIf we remove   All  and keep the IP   123.45.67.89 , all traffic from this IP sent to port   443  on your router will be forwarded to port   3000  on the local IP   192.168.1.50 .  If we remove the IP and keep   All , then all traffic from the internet on port   443  will be redirected to port   3000  on   192.168.1.50 .  This is useful when you have a server that must be accessible from the internet. For instance, a website uses port   80  (non-secure) or   443  (SSL-secured).  \nTo make the website accessible, you'll configure your router to redirect the domain request to your local server.  \nAssume your service runs on port   3000  locally (  http://192.168.1.50:3000 ), you would redirect all traffic from port   443  on the router to port   3000  on the local server.      Warning:  If you have multiple services to expose like   subdomain1.mydomain.com  and   subdomain2.mydomain.com , your router cannot differentiate requests and forward to different ports.  \nYou must use a   Reverse Proxy  to route traffic based on the request.  DHCP   Every time a device connects to your local network, your router assigns it an IP address using DHCP rules.  \nThis IP is randomly selected from a predefined pool.  \nAt every device reboot, the IP may change — which is problematic if you're forwarding ports, as the target IP may no longer be valid.  To avoid this, use your router's DHCP server to assign a static IP address.  Each device has a physical \"MAC address\".  \nTo assign a fixed IP, you must know your device's MAC address (visible in your router when it's connected), and assign it a static IP.  \nThis is called a \"static DHCP lease.\"  That way, your machine's IP never changes and your port forwarding rules remain effective.     Variable  Description  Example     IP  Fixed local IP to assign   192.168.1.50    MAC Address  Physical address of the device   5E:FF:56:A2:AF:15  For more information, refer to your router's documentation.",{"id":30,"path":31,"dir":22,"title":32,"description":7,"keywords":33,"body":36},"content:2.general:1.networking:2.dns.md","/general/networking/dns","DNS Zone",[34,35],"Introduction","The DNS Zone","   Domain Names and DNS Zones   🎯    Objectives:   Understand how a DNS server works  Learn how to edit a DNS zone  Introduction   When you browse a website or use an app, requests are made to one or more domains to fetch content for the page. Your device doesn't know the IP addresses of these servers, so it contacts a   name server  (Domain Name Server), which responds with the most up-to-date IP address for the domain being requested.  The DNS zone is like a registry with signposts that direct your requests to the correct destination.    The DNS Zone   When you purchase a domain from a registrar (Cloudflare, OVH, etc.), the registrar assigns you a DNS zone that you can customize.  You can enter   records  into this DNS zone to direct requests properly. You can find   more information here .  Example of a DNS zone for the domain   mydomain.com :   @ IN SOA ns1.dns.me. dns.net. (2024051800 86400 3600 3600000 60)\n                 IN NS     ns1.dns.me.\n                 IN NS     ns2.dns.me.\n                 IN A      203.0.113.0\nwww              IN CNAME  mydomain.com\nsousdomaine      IN CNAME  mydomain.com\n  In this example:    $TTL 3600  tells global name servers that the records are valid for 1 hour (after which they need to re-check).   IN SOA ns1.dns.me. dns.net. (...)  indicates   ns1.dns.me  as the primary DNS server, with refresh intervals.   IN NS  records define the authoritative name servers for the domain.   IN A 203.0.113.0  means   mydomain.com  points to IP   203.0.113.0 .   subdomain IN CNAME mydomain.com  means   subdomain.mydomain.com  points to the same destination as   mydomain.com .  So, if you want to point   mydomain.com  to your server, you can do it by adding an   A  record pointing to your server's public IP address.      Warning:  If your server is hosted at home:   Your public IP is the one assigned to your home router. Make sure it's static, or configure   DDNS .  Make sure you've   set up port 443 forwarding to your server's listening port .  If you're adding a subdomain that should also point to your server, use a   CNAME  record pointing to   mydomain.com .      Why not use an   A  record for the subdomain?  If your subdomain points to the same server as   mydomain.com , it's better to use a   CNAME  record because if the server's IP changes, you won’t need to update the subdomain record.  Most registrars offer user-friendly interfaces to manage DNS records. Refer to your registrar’s documentation for specific instructions.",{"id":38,"path":39,"dir":22,"title":40,"description":7,"keywords":41,"body":44},"content:2.general:1.networking:3.samba.md","/general/networking/samba","Samba",[42,43],"Sharing a Network Folder","Accessing a Shared Folder","   Samba  Samba is a protocol that allows access to a folder located on a network drive. It can be configured on macOS, Windows, or Linux.  There are many tutorials for setting up Samba on Windows or on NAS systems like Synology, but here we focus on Debian.   🎯   Objectives:   Create a network folder on a remote machine  Access the network folder from our server    Sharing a Network Folder      In this example, we will share the   /video  folder from a remote machine called   remote-machine . We will access this folder from a machine called   local-machine . The user connecting to the network drive will be   sambauser .  Install Samba Server     sudo   apt   update   &&   sudo   apt   upgrade\n   sudo   apt   install   samba   smbclient   cifs-utils\n  Create the   /video  Folder     sudo   mkdir   /video\n  Configure the Share  Now, edit the file   /etc/samba/smb.conf .   ✨   Tip:  You can use   File Browser  to navigate and edit your files instead of using terminal commands.     sudo   vim   /etc/samba/smb.conf\n  Find the   workgroup  variable, press   i  to enter insert mode, and name your workgroup (e.g.,   workgroup = WORKGROUP ).  Then scroll to the end of the file and add the following configuration:     [video]\n      comment   = Video folder\n      path   = /video\n      writable   = yes\n      guest   ok   = no\n      valid   users   = @smbshare\n      force create   mode   = 770\n      force directory   mode   = 770\n      inherit   permissions   = yes\n  Press   Esc  to exit insert mode, then type   :x  and press   Enter  to save and exit.  Create a Samba User and Group  Since we're using a secured share, we need to create a user and group to access it remotely.  Create the group:     sudo   groupadd   smbshare\n  Give the group control over the   /video  folder:     sudo   chgrp   -R   smbshare   /video\n  Set inherited permissions:     sudo   chmod   2775   /video\n  Now add a no-login user — this user cannot log into the server but can access Samba.     sudo   useradd   -M   -s   /sbin/nologin   sambauser\n  Add the user to the   smbshare  group:     sudo   usermod   -aG   smbshare   sambauser\n  Set a Samba password:     sudo   smbpasswd   -a   sambauser\n  Enable the Samba account:     sudo   smbpasswd   -e   sambauser\n     sudo   ufw   allow   from   remote-ip   to   any   app   Samba\n   ::\n  Accessing a Shared Folder   ::  Install Required Packages     sudo   apt   update   &&   sudo   apt   upgrade\n   sudo   apt   install   cifs-utils\n  Create the Mount Destination  We will create a folder on our local machine where the remote   /video  folder will be mounted — e.g.,   /mnt/video .     sudo   mkdir   /mnt/video\n  Prepare the .credentials File  To avoid typing our username and password every time, create a   .credentials  file storing the login info.  Create it in the   /smb  folder:     sudo   mkdir   /smb\n   sudo   vi   /smb/.credentials\n  Enter insert mode (  i ) and write:     username  =smbuser\n   password  =password\n    smbuser : the user we created on the   remote-machine   password : the password set earlier  Press   Esc , then   :x  and   Enter  to save and exit.  Set proper file permissions:     sudo   chmod   600   /smb/.credentials\n  Mount the Shared Folder  Now mount the folder:     sudo   mount   -t   cifs   -o   credentials=/smb/.credentials   //remote-ip/video   /mnt/video\n  Replace   remote-ip  with your   remote-machine 's IP address.  Verify the mount:     sudo   mount   -t   cifs\n  You’ll see details confirming the mount is successful.  Now you can access the   /video  folder of the   remote-machine  from your   local-machine !  Auto-mount on Boot  By default, shares aren't auto-mounted after reboot. To automate this, edit the   /etc/fstab  file.  First, back it up:     sudo   cp   /etc/fstab   /etc/fstab.bak\n  Then add the mount configuration line:     sudo   echo   //remote-ip/video      /mnt/video    cifs     _netdev,nofail,credentials=/smb/.credentials,x-systemd.automount,x-systemd.device-timeout=  15   0   0   >>   /etc/fstab\n  Reboot the machine:     sudo   reboot\n  After rebooting, verify the mount:     sudo   mount   -t   cifs\n  And done!  Unmount the Shared Folder     sudo   umount   -t   cifs   /mnt/video\n  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":46,"path":47,"dir":48,"title":49,"description":7,"keywords":50,"body":53},"content:2.general:2.storage:1.raid.md","/general/storage/raid","storage","RAID",[51,52],"Different Types of RAID","Software RAID","   RAID   Redundant Array of Independent Disks  In computing, RAID (Redundant Array of Independent Disks) is a system that allows multiple hard drives to be combined to improve performance and/or reliability. It works by restructuring and distributing data blocks across the drives.  Originally, RAID systems were hardware-based, meaning a dedicated controller (a specific chip) managed data distribution and RAID operations. Today, most RAID systems (or their equivalents) are software-based. In fact, many software technologies can create RAID-like systems with features not available in hardware RAID, such as automatic repair (data scrubbing), snapshots, and more.  Different Types of RAID  There are several types of RAID, each offering its own pros and cons. In general, RAID impacts the following five factors:   Number of drives  Total storage capacity  Read speed  Write speed  Fault tolerance (resistance to hardware failure)     RAID is not a backup system but a service continuity system! It only allows hot-swapping of drives without interrupting your server or restoring from backup. You still need an external backup system.  No RAID   \n    \n    \n      Just your disks, without RAID. Data is stored disk by disk. \n      If you lose a disk, only its data is lost. \n      Total capacity is the sum of all disks.  Use your disks without RAID when you're not afraid of data loss and can tolerate service interruptions between failure and backup restoration.  RAID 0   \n    \n    \n      OS sees 1 drive. \n      Data is striped across all disks. \n      If you lose one disk, you lose all data. \n      High read and write performance (multiplied by number of disks). \n      Total capacity is the sum of all disks. \n      Minimum of 2 disks required.  Use RAID 0 when you prioritize performance and are not concerned about data loss. Ideal for temporary, high-speed storage (video editing, AI workloads, etc). Not suitable for long-term storage, as one failure means total data loss.  RAID 1   \n    \n    \n      OS sees 1 drive. \n      All disks contain identical data. \n      You can lose all but one disk. \n      Improved read speed (scales with number of disks). \n      Total capacity is equal to one disk (e.g., 2×10TB = 10TB). \n      Minimum of 2 disks required.  Use RAID 1 for strong redundancy. Each disk contains all data, so performance remains unaffected during a failure. Once failed disks are replaced, data is quickly restored. However, usable storage is limited to one disk’s capacity, making it an expensive solution.   ✨   Tip:  You can combine RAID 1 with other RAID types to create mirrored arrays.  RAID 5   \n      OS sees 1 drive.  Data is striped with parity blocks for redundancy.  You can lose 1 disk and recover data.  Improved read speed (scales with number of disks).  Total capacity is the sum of all disks minus one (e.g., 3×10TB = 20TB).  Minimum of 3 disks (4 recommended to reduce capacity loss).  Use RAID 5 when you want reliable storage with 3 to 5 disks and minimal space loss. It tolerates one disk failure but may have degraded performance during recovery, which can take days.  RAID 6   \n      OS sees 1 drive.  Data is striped with dual parity blocks.  You can lose 2 disks and still recover data.  Improved read speed (scales with number of disks).  Total capacity is the sum of all disks minus two (e.g., 4×10TB = 20TB).  Minimum of 4 disks (6 recommended to minimize space loss).  Use RAID 6 in similar situations as RAID 5, especially with 6 or more disks. More disks mean higher failure risk. RAID 6 offers peace of mind by tolerating two simultaneous failures.  Software RAID  (coming soon)",{"id":55,"path":56,"dir":48,"title":57,"description":7,"keywords":58,"body":61},"content:2.general:2.storage:2.zfs.md","/general/storage/zfs","ZFS",[59,60],"Structure","Why ZFS over others?","   ZFS   🎯   Objectives:   Understand what ZFS is and why it's useful  ZFS is widely used in the world of servers, NAS systems (like FreeNAS / TrueNAS), virtualization, and even by tech-savvy individuals who want reliable storage. It is both a   file system  (like NTFS for Windows, EXT4, FAT32, etc.) and a   volume manager  (similar to LVM).  To put it simply:   A   volume manager  organizes physical storage (like one or more hard drives).  A   file system  organizes how data blocks are written, read, and deleted within those volumes.  ZFS goes far beyond traditional file systems in terms of performance and features.  \nHere’s what we’re most interested in:   Its   snapshot management  features, allowing you to quickly roll back in case of issues.  Its support for disk groupings and    RAID-like structures  (Z-Mirror, RAIDZ1, RAIDZ2, RAIDZ3).  Its   automatic recovery of corrupted data  (through scrubbing).  Its performance, enhanced by RAM caching (ZFS ARC).  Its robust error notifications and monitoring.  Structure     ZFS has a unique structure:    vdev  (virtual device): a group of physical or virtual disks.   zpool : a collection of vdevs configured as a single storage pool. A zpool can contain multiple vdevs, but a vdev belongs to only one zpool.   dataset : a logical data container within a zpool. Each dataset can have its own settings (compression, quotas, permissions, etc.).  There are several dataset types:    file system : a standard ZFS filesystem, mounted without storage quotas.   zvol : a \"virtual disk\" with a defined size, which you can format and partition as if it were a physical disk.   snapshot : a frozen-in-time version of another dataset. Snapshots can be created manually or through backup tools. They can be mounted to browse data as it was at the snapshot time.  Why ZFS over others?   Data Integrity  ZFS continuously checks that your stored data hasn't become corrupted. Every block of data is associated with a checksum, allowing ZFS to detect even the smallest alteration. If corruption is found and a healthy copy exists elsewhere, ZFS can repair the data automatically.  Built-in RAID  ZFS includes its own volume management system (vdevs). You can build a zpool using multiple disks—similar to traditional   RAID  setups—but with more flexibility. For example:    Z-mirror  → equivalent to RAID 1   RAIDZ1  → equivalent to RAID 5 (tolerates 1 disk failure)   RAIDZ2  → equivalent to RAID 6 (tolerates 2 disk failures)   RAIDZ3  → tolerates up to 3 disk failures  ZFS handles all this natively—no external RAID software needed.     Check out the   article on RAID  to find the right solution for your needs.  Snapshots and Clones  ZFS allows you to create snapshots—instantaneous images of a dataset's state. Snapshots take up minimal space and can be scheduled frequently. You can also create clones: writable copies of snapshots.  Compression and Deduplication  ZFS can compress data on the fly (transparently to the user), saving disk space. It also supports deduplication (removing duplicate data), though this feature requires a lot of memory and is not recommended for all use cases.   Now you know why ZFS is   the  file system to deploy on your NAS.",{"id":63,"path":64,"dir":65,"title":66,"description":7,"keywords":67,"body":72},"content:2.general:3.hardware:1.basics.md","/general/hardware/basics","hardware","The Basics",[68,69,70,71],"Motherboard","CPU","RAM","GPU","   Server Basics   🎯   Objectives:   Understand the fundamentals of server hardware    A   server  is essentially a computer dedicated to specific tasks, designed to remain accessible at all times. Structurally, it's not much different from a regular computer. Depending on its intended use, some components may vary. This article serves as a reference to help you understand the essential components of a server and how their roles adapt based on your needs.  Motherboard   The   motherboard  is the foundation of your machine. It's the component that connects all others together. It enables communication between components and interaction with peripherals (keyboard, mouse, etc.). Choose it based on your I/O (Input/Output) needs like USB ports, network ports, speed, etc., and ensure compatibility with the components you plan to install.  Key components connected to the motherboard:   CPU  RAM  Storage (HDD and/or SSD)  Optional dedicated GPU  Common consumer motherboard formats:   E-ATX: largest  ATX: standard  Micro-ATX: smaller  Mini-ITX: smallest  Larger boards generally offer more ports and features. Pre-built systems might use proprietary formats.  CPU   \n    \n    The   CPU  (Central Processing Unit) is the computer's calculator. It processes most software tasks. Modern CPUs have multiple cores, often with virtual threads, to better handle workloads. They need to be cooled using either an active cooler (with a fan) or a passive one (fanless), depending on power consumption (watts). Choose your CPU based on how you plan to use the server.      Caution:  Ensure third-party coolers are compatible with the CPU socket and always apply thermal paste before installing the cooler.  Consider:   Number of cores (more cores = better multitasking)  Clock speed in GHz  Power consumption in Watts  For low-power home servers or NAS (non-intensive computing), consider Intel N100/150 (4 cores) or N305/N355 (8 cores)—efficient and low power (ideal for 24/7 uptime).  RAM   \n      RAM  (Random Access Memory) is fast, temporary memory used by the CPU (and iGPU if applicable) for quick access during execution. It clears periodically and when the machine powers down. Better RAM = better CPU performance.  Comes as sticks installed on the motherboard. Varies by format and generation (currently DDR5).  GPU   The   GPU  (Graphics Processing Unit) handles graphical, video, and sometimes AI-related processing. Its main theoretical use is to display the image on your screen. In servers, it's useful for media centers (e.g.   Plex ) and for accelerating AI tasks like facial recognition or photo indexing (e.g.   Immich ).  Depending on the required performance, one can choose between a dedicated GPU with its own VRAM (a graphics card connected to a PCIe slot on the motherboard), or an iGPU—an integrated GPU built into the CPU (such as the N100/N150 or N305/N355), which uses the system’s shared RAM.  HDD(s)   \n     An   HDD  (Hard Disk Drive), or hard drive, is a component used to store data. It was once the standard storage device in computers. HDDs consist of one or more stacked platters and read/write heads—somewhat like a vinyl record player.  Today, HDDs can store enormous amounts of data (up to 30TB, or 30,000 gigabytes, for consumer models), but their read and write speeds are limited due to their mechanical nature. They are also bulky and heavy.  Generally, HDDs are best suited for storing data that doesn’t require frequent access or fast write speeds, such as media files (videos, photos), cloud drives, or archived data. They perform well in these scenarios and, most importantly, are significantly cheaper than SSDs for the same amount of storage.   ✨   Tip:  Use multiple HDDs in   RAID  to enhance performance and redundancy.  Comes in 3.5\" and 2.5\" formats; servers usually favor the more reliable 3.5\".  SSD(s)   \n     An   SSD  (Solid State Drive) is a small circuit board with memory chips soldered onto it, used to store information. Unlike RAM, these chips retain data even when not powered, meaning the information is preserved after a reboot. SSDs are generally used as the main storage medium for your server.  Unlike HDDs, SSDs have no moving parts, are highly compact, and most importantly, are extremely fast—offering speeds of several gigabytes per second for high-performance models.  SSDs come in various formats, but today the preferred choice is the M.2 NVMe version, as it is the smallest, fastest, and has become the standard on modern motherboards.  However, SSDs are significantly more expensive than hard drives for the same storage capacity. Typically, the operating system (OS) is installed on the SSD to ensure fast performance. In a server environment, it's also ideal to store   Docker containers  and databases on the SSD. More broadly, any data that needs to be accessed frequently and quickly—such as websites, applications, or processing workloads—should be stored on an SSD.  Network Card   A   network card  allows your machine to communicate with your network (including the internet). It consists of a controller chip and one or more network ports. These ports—often Ethernet ports—can come in different physical formats and support various data transfer standards:    RJ45 Gigabit Ethernet (10/100/1000):  The standard RJ45 connector, supporting speeds from 10 Mbps (0.125 MB/s) up to 1000 Mbps (125 MB/s).   RJ45 2.5G:  Same connector type, supporting up to 2.5 Gbps (2,500 Mbps or 312.5 MB/s).   RJ45 5G:  Same connector, supporting up to 5 Gbps (625 MB/s).   RJ45 10G Base-T:  Same RJ45 format, supporting up to 10 Gbps (1.25 GB/s).   SFP 1G:  SFP port, commonly used for fiber optic connections, supporting speeds up to 1 Gbps.   SFP+ 10G:  An enhanced version of the SFP port, also used for fiber optics, supporting up to 10 Gbps.      Caution:  Match network gear (router, switch, cables) to your desired speed. For most uses, CAT5E cables are enough; use CAT6A beyond 10 Gbps. Fiber requires additional care (simplex, duplex, transceivers...).  The network card is usually built directly into the motherboard, but you can also use dedicated network cards, for example via USB or a PCIe expansion slot.  In general, for a server setup, it's recommended to have at least two Ethernet ports to ensure redundancy in case one connection fails.  Input/Output Ports    I/O  ports allow communication with external devices (displays, keyboard, mouse, network...). Motherboards typically offer:   Ethernet ports  USB ports (varied types/speeds)  Video ports  Audio jacks  Choose a motherboard and expansions based on your I/O needs.  Power Supply   The   power supply unit  (PSU) is the component that provides electrical power to your machine’s components. It connects to the wall via a power cord and has several output cables that plug into the motherboard and various peripherals, such as hard drives or dedicated graphics cards.  A power supply is defined by several key characteristics:   Wattage (its total power output),  Modularity (whether the cables are fixed or detachable),  Efficiency (measured as a percentage). For example, a 500W PSU with 80% efficiency will actually draw 625W from the wall to deliver 500W to the system.  Another important factor is the form factor. There are several standard sizes, from ATX L (for larger cases) to SFX (for compact builds). There are also specialized models for rack-mounted servers, which are typically flat and space-efficient.  To choose the right PSU, a common rule of thumb is to estimate your system’s power needs based on usage, and then double that value. This is because most power supplies operate at optimal efficiency around 50% of their maximum load.  Case   \n    \n    The   case  is also an essential component of your machine. It plays a key role in cooling, through its fans and airflow design, and it determines the form factor compatibility for your motherboard, power supply, and any dedicated GPU you may install.\n  Additionally, the case dictates how many HDDs you can install and what formats they support. Some cases are rack-mountable, meaning they can be installed in server racks (server cabinets).  Choose your case carefully based on your specific needs and the hardware you plan to use.",{"id":74,"path":75,"dir":65,"title":76,"description":7,"keywords":77,"body":81},"content:2.general:3.hardware:2.network.md","/general/hardware/network","Network",[78,79,80],"The Router","The Switch","Cables","   Network   🎯   Objectives:   Understand the basics of networking hardware    A computer network cannot exist without the hardware required to build it. Hardware determines the size of the network, communication speeds, and its overall performance. In this article, we will focus on the simplest types of networks, typically found in home environments.  The Router   The   router  is the central hub of your network. It directs   packets —the blocks of data that travel across your network—from the sender to the appropriate recipient. It manages the routing of data both within your local network and to/from external networks. In short, it enables devices to communicate with each other and with the internet.  Everyone has a router at home—it's the   internet box  provided by your ISP (Internet Service Provider).  In general, a router consists of:   a WAN (Wide Area Network) port that receives data from the internet (or from a higher-level network). For example, it could be a port for a fiber optic connection from your ISP, or an SFP+/RJ45 port for a third-party router.  a switch, i.e., a hub with several   LAN  (Local Area Network) ports allowing multiple devices to connect to your network. These ports can be RJ45 or SFP/SFP+.  sometimes a built-in WiFi transmitter/receiver.  A router may also include   firewall  capabilities, allowing you to restrict traffic from specific devices, as well as    NAT (Network Address Translation)  for port forwarding. It generally includes a    DHCP (Dynamic Host Configuration Protocol)  server to automatically assign   IP addresses  to devices connected to the network.  The router directly affects communication speeds between devices. The WAN port limits the maximum internet speed you can receive from your ISP. For example, if your subscription offers 5 Gb/s, you’ll need a WAN port that supports at least 5 Gb/s. Likewise, internal device-to-device communication is limited by the speed of the switch. If your devices communicate at 5 Gb/s, the router’s switch must have 5 Gb/s ports. If you're using WiFi 7 equipment and want to enjoy its full speed, your router must support it as well. If you’re using a separate WiFi access point, make sure its network port matches or exceeds the speed of the WiFi it broadcasts—and that the router supports it too.  Internet speed, number of devices, WiFi speed, and internal network speed—these are the four key factors to consider when choosing an internet box or buying your own router.   ✨   Tip: \nYou can easily use a third-party router to manage your network if your ISP’s internet box supports   bridge mode . In France, only the provider Free offers this option. It is technically possible with other providers that do not support bridge mode, but it can be quite difficult and may prevent you from using all the features a third-party router provides.  The Switch   The   switch , or network switch, is a device that allows multiple devices to connect to the network. It acts as a literal hub, connecting directly to the router or to another switch upstream. It helps avoid overloading the switch ports on your router or relocating devices to another room without running a cable from each one back to the router. Another common use case is to segment multiple networks that are managed by the same router.  There are generally two types of switches:    Unmanaged switches , the most common. These are plug-and-play: you just plug them in and everything works automatically.   Managed switches . These offer a configuration interface (via command line or web UI), allowing you to fine-tune routing rules under the control of the router. They are powerful for creating virtual networks between your devices, but usually require more setup time and are less convenient than simple unmanaged switches.      Warning:  Make sure to use a switch with ports that match the speeds supported by your network devices.  Cables   Cables are essential components of your network. Depending on their type and category, they can limit the bandwidth between devices, so they must be chosen to match your network's specifications. They also need to be compatible with your devices' ports.  Here’s a quick reference of the most common cable and port standards:    RJ45 Gigabit Ethernet 10/100/1000 : The standard RJ45 connector, supporting speeds from 10 Mbps (0.125 MB/s) to 1000 Mbps (125 MB/s)   RJ45 2.5G : Same connector, supporting speeds up to 2.5 Gbps (312.5 MB/s)   RJ45 5G : Same connector, supporting speeds up to 5 Gbps (625 MB/s)   RJ45 10GBase-T : Same connector, supporting speeds up to 10 Gbps (1.25 GB/s)   SFP 1G : SFP port, typically used for fiber optics, supporting up to 1 Gbps   SFP+ 10G : Enhanced SFP port, also for fiber, supporting up to 10 Gbps  Ethernet Cables  These copper cables usually use the standard   RJ45  connector. It's the most common network connector found on routers and switches.  Ethernet cables are divided into categories that define their maximum speed based on distance:     Speed  Cable Type  Max Distance    10 Gb/s  CAT 6A  100 m    CAT 6  55 m    CAT 5e  30 m   5 Gb/s  CAT 6  100 m    CAT 5e  30 m   2.5 Gb/s  CAT 5e  100 m   1 Gb/s  CAT 5e  100 m   100 Mb/s  CAT 5  100 m  Some of these cables are flat, round, shielded (requiring grounding), etc. Choose based on your setup. What matters is that, for example, if you want to connect a device with a 2.5 Gb/s RJ45 port to a 2.5 Gb/s router, you’ll need at least a   CAT 5e  cable.  On the other hand, if your device is limited to 100 Mb/s, a simple   CAT 5  cable will suffice.  Nowadays, in new buildings, it is standard practice to install   CAT 6A  cables inside walls. This way, wall ports are ready to support 10 Gb/s over 100 meters.   Optical Cables  Very thin but fragile, optical cables are increasingly appearing in home networks. It often starts with the fiber cable connecting your ISP’s outlet to your box/router. They have several advantages:   Extremely compact  Zero electrical consumption (unlike copper, which loses energy as heat)  No electromagnetic radiation (no shielding needed, no signal interference)  Very high speeds over long distances  For local networking, it's important to understand that several types of fiber cables exist. Their performance depends on both distance and compatibility with the appropriate   transceiver . Fiber cables connect to your devices' SFP+ ports via a small device called a transceiver, which converts electrical signals to light (and vice versa).  For local networks, the recommended standard is a   multimode OM3 fiber with LC connectors , paired with a   10G LC SFP+ transceiver . This setup allows 10 Gb/s connections and is compatible with most devices featuring SFP+ ports.      Warning:  Make sure to use transceivers that are compatible with your devices (routers, switches, or other hardware). There is no universal standard yet, and manufacturers usually specify which brands are supported.   DAC Cables  These are copper cables with integrated   transceivers . They allow two SFP/SFP+ ports to communicate over short distances without using fragile fiber or RJ45 adapters. However, they consume more energy due to natural copper loss, which is non-negligible.   SFP+ Transceivers  These let you connect different types of cables to your SFP/SFP+ ports. Variants are available for:   Fiber optic  DAC  RJ45     RJ45 transceivers consume a lot of energy due to copper signal loss and can generate significant heat. Low-power models (under 2W) exist and are generally rated for longer cables (e.g., 80m instead of 30m). Surprisingly, these are preferred over short-distance models because they generate less heat and consume less energy—making them more compatible with sensitive devices. Using the wrong type can cause network degradation or even outages.",{"id":83,"path":84,"dir":65,"title":85,"description":7,"keywords":86,"body":91},"content:2.general:3.hardware:3.prolonas.md","/general/hardware/prolonas","The ProloNAS",[87,88,89,90],"Example Hardware","Why a NAS?","The Problem with Consumer NAS Systems","OK, but how do I turn my Mini PC Serveex into a ProloNAS?","   The ProloNAS  When you decide to dive into the adventure of running your own home server, the same questions usually come up:   “Where should I start?” ,   “Isn’t it expensive?” . And often, people either give up or end up buying a proprietary NAS that they’ll throw away a year later once they realize it only brings headaches and wasted money.  A server isn’t a piece of furniture. It’s simply any computer capable of running Linux.That’s why mini PCs powered by   Intel N100  processors are so popular: for around $100–130 on Chinese platforms, you can get a machine that runs   24/7  for years, capable of handling everything you’d expect from a home server or personal cloud without sacrificing performance.  It’s   objectively inexpensive , and anyone with a bit of curiosity can get started.  A mini PC for $100 + a USB dock for $50 that holds multiple hard drives = a complete platform for $150, versus   $350–1200  for branded NAS systems.  That’s all a   ProloNAS  is. It’s then up to you to scale your storage capacity according to your needs.    Example Hardware   Mini PC —   Note: choose 16 GB / 512 GB :   Aliexpress  DAS (Direct Attached Storage) —   Note: select “EU plug” :   Aliexpress  More refined alternative with a fan:   Amazon    These are not affiliate links — buy wherever you prefer.  Why a NAS?  A   NAS  (Network Attached Storage) is a machine centered around storage, designed to be shared over a network.The idea is to have a   reliable and secure  storage space that serves as the backbone for your personal services and apps such as a self-hosted cloud like   Nextcloud , a photo sync tool like   Immich , or a media server like   Plex . You can also store camera footage, backups, or even development projects on it.  But why not just use a mini PC with an external hard drive?  Sure, a simple mini PC with 1–2 TB of storage will do for most people.And your movie collection might fit on an external drive of a few extra terabytes. But that’s   neither reliable nor scalable  a single shock or hardware failure could permanently destroy your data.  A real NAS is built around   storage reliability . It uses redundancy strategies like   RAID  to protect against drive failure, and snapshot systems like   ZFS  to guard against corruption.  In short, a NAS lets you   host everything yourself  that you currently entrust to third parties while maintaining control, reliability, and data safety.  The Problem with Consumer NAS Systems  Many brands offer “ready-to-use” NAS platforms: Synology, QNAP, Ugreen, and others. They promise simplicity and sleek web interfaces, but the reality is quite different.  First, the price.  $350 is the starting price for a 2-bay NAS (without drives) from Synology. For that, you get a 2019 processor, no SSD slot for the OS, and a measly 2 GB of RAM.  Now, compare that to the   ProloNAS : an N100 (4 cores), 16 GB RAM, a 512 GB SSD for $100–130, plus a 4-bay DAS for $55. That’s   half the price  of a 2-bay Synology, and   a quarter of the price  of a 4-bay one.  Locked-Down Operating Systems  “Yeah, but at least with a Synology, you plug it in and everything just works.”  One year.  \nThat’s how long it took before I threw away my Synology and realized I should have started with a   ProloNAS  (which later became a full-fledged server).  Manufacturers ship heavily customized Linux-based OSes: ancient kernels, limited app repositories, and complete dependence on their proprietary tools. As a result, you can’t fully tailor your NAS to your needs, and many Docker containers simply won’t run because the kernel is too old.  Total Vendor Lock-In  “I’m fine with the built-in apps.”  \nYeah, I thought so too… until my needs exploded: media center, password manager, Git hosting, strong authentication, web hosting, and more.  Why stay stuck with half-baked proprietary tools when you can rely on   open-source projects  that are regularly updated and interoperable?  And what happens when the manufacturer decides to drop support or limit hardware compatibility? It’s already happened, Synology made certain drives   incompatible  unless they were “certified” by them. They even   disabled hardware transcoding  on their NAS units:   see here .  In short, you have   no control  over a product that isn’t open, nor truly yours.  OK, but how do I turn my Mini PC Serveex into a ProloNAS?  As mentioned earlier: by adding a   DAS (drive hub)  and setting up a redundant storage system with   RAID  and   ZFS , you can transform your mini PC into a robust and scalable NAS.  Enjoy !",{"id":93,"path":94,"dir":95,"title":34,"description":7,"keywords":96,"body":99},"content:3.serveex:1.introduction.md","/serveex/introduction","serveex",[97,98],"A Home Lab by a Beginner, for Beginners","Coming Soon","   A Home Lab by a Beginner, for Beginners     Serveex  is primarily a personal project aimed at hosting as many everyday services as possible at home, without relying on proprietary platforms (Google, Apple, Netflix, etc.). The goal was to experiment, learn, and document every step along the way. This is purely a scientific project and is not intended for production use.  A big thanks to   Nipah  for sharing his infinite knowledge and, above all, for his patience.    Prerequisites:    Have   an online VPS  or a local machine: ideally a mini PC (you can find N100 models for around €100), but it also works on a laptop or   a virtual machine . The   Freebox Delta/Ultra offer virtual machines .  Know how to configure   NAT rules on a router and assign DHCP leases  Know how to configure the   DNS zone of a domain name    The goal is to be easily deployable and easy to migrate, so here is its structure:    The Core of the Server       Operating System    Install and configure Debian 13     Container Engine    Install Docker     Docker GUI    Install and deploy Dockge     Reverse Proxy    Expose your services with SWAG    Security       VPN    Install and deploy Wireguard     SSO & MFA    Install and deploy Authentik     Zero Trust    Install and deploy Cloudflared    Monitoring       Service Status    Install and deploy Uptime-Kuma     Log Management    Install and deploy Dozzle     Connection Management    Install and deploy Speedtest Tracker     Resource Status    Install and deploy Beszel     Wake on Lan    Install and deploy UpSnap    Media       Media    Install and deploy Plex     Seedbox    Install and deploy Qbittorrent    Cloud Drive & Photos       Drive    Install and deploy Nextcloud     Photos    Install and deploy Immich    Files & Sharing       File Explorer    Install and deploy file-browser     Sharing    Install and deploy Pingvin    Development Tools       Visual Studio Code    Install and deploy code-server     Git Repository    Install and deploy Gitea     Tools    Install and deploy IT Tools    Useful Applications       Ad-blocking DNS and Filters    Install and deploy Adguard Home     Password Manager    Install and deploy Vaultwarden  Coming Soon    Homepage, to have all your services at a glance and access them easily  Mkdocs for your documentation  Docus, an alternative to Mkdocs  UpSnap to remotely wake your machines",{"id":101,"path":102,"dir":103,"title":104,"description":7,"keywords":105,"body":109},"content:3.serveex:2.core:1.installation.md","/serveex/core/installation","core","Debian 13",[106,107,108],"Installation","Must-Have CLI Apps","Useful Features","   Debian 13   🎯   Goal:  Install Debian 13 and the main dependencies to have a ready-to-use OS, accessible via SSH.    Installation    [BIOS Setup]((  https://www.debian.org/releases/stable/i386/ch03s06.en.html#bios-setup )   Download Debian Image   Create Bootable USB (Rufus)   Install Debian and Set Up SSH  Install sudo and add a user to the sudo group for administrative privileges.\nLog in as root:     su   -\n  Enter your password, then type:     apt   install   sudo\n  Add the user to the sudo group:     adduser   \u003C  usernam  e  >   sudo\n  Next time the user logs in, they will be able to use the   sudo  command to execute commands with administrative privileges.   Everything About Remote Console Access (SSH)  Optional -   UPS Client in Case of Power Outage  /   also here  Optional - Wake up after power outage → configure BIOS S0 state  Optional -   Wake Server Remotely (WoW - WoL)  Must-Have CLI Apps   Some essential apps you’ll likely need at some point, so might as well install them early:     sudo   apt   update\n   sudo   apt   upgrade\n   sudo   apt   install   vim   btop   ranger   git   duf   neofetch   samba   cifs-utils   tree   unzip\n  Additionally:    gping  - Graphical ping tool   lazydocker  - CLI Docker container manager  Useful Features   Firewall    ufw   Firewalld  Samba Sharing (Access a Remote Network Disk)    Create and Access a Samba Share  File Transfer via rsync     sudo   rsync   -avhHSP   /source   /destination\n     Add     --exclude   @eaDir  if the source is a Synology NAS  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":111,"path":112,"dir":103,"title":113,"description":7,"keywords":114,"body":118},"content:3.serveex:2.core:2.docker.md","/serveex/core/docker","Docker",[115,116,117],"Install Docker","Install Dockge to manage and deploy containers","Watchtower, to auto-update containers","   Docker  Docker, to install deployable services in seconds and manage them with just a few commands or clicks.   🎯    Goals:   Install   Docker  Install   Dockge  to manage stacks  Install   Watchtower  to update containers    Install Docker   Add the Docker repositories and GPG key:     # Add Docker's official GPG key:\n   sudo   apt-get   update\n   sudo   apt-get   install   ca-certificates   curl\n   sudo   install   -m   0755   -d   /etc/apt/keyrings\n   sudo   curl   -fsSL   https://download.docker.com/linux/debian/gpg   -o   /etc/apt/keyrings/docker.asc\n   sudo   chmod   a+r   /etc/apt/keyrings/docker.asc\n   \n   # Add the repository to Apt sources:\n   echo     \"deb [arch=$(  dpkg   --print-architecture  ) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian   $(  .   /etc/os-release &&   echo   \"  $VERSION_CODENAME  \") stable\"   |     sudo   tee   /etc/apt/sources.list.d/docker.list   >   /dev/null\n   sudo   apt-get   update\n  Install the packages:     sudo   apt-get   install   docker-ce   docker-ce-cli   containerd.io   docker-buildx-plugin   docker-compose-plugin\n  That's it!   More options:    Install Docker for Debian 13     From here on, we assume the stacks are installed in the   /docker  folder, created using the command:     sudo   mkdir   /docker\n  Install   Dockge  to manage and deploy containers    Dockge  is a web tool to create, configure, launch, and manage Docker containers. It's a simple, intuitive interface that’s lighter and easier for beginners than using the CLI or Portainer.    Configuration  File structure we will create:     root\n   └──   docker\n       └──   dockge      \n           └──   compose.yml\n  Create the stack folder:     cd   /docker\n   sudo   mkdir   dockge\n  Then create the   compose.yml  file in this folder using   vim :     cd   /docker/dockge\n   sudo   vi   compose.yml\n  Press   i  to enter insert mode and paste the following:     ---\n   services  :\n     dockge  :\n       image  :   louislam/dockge:1\n       restart  :   unless-stopped\n       container_name  :   dockge\n       ports  :\n         -   3555:5001   # LAN-accessible port will be 3555\n   \n       volumes  :\n         -   /var/run/docker.sock:/var/run/docker.sock\n         -   /docker/dockge/data:/app/data\n         -   /docker:/docker\n       environment  :\n         -   DOCKGE_STACKS_DIR=/docker\n  Press   Esc  and type   :x  to save and exit.  To launch the container:     cd   /docker/dockge\n   sudo   docker   compose   up   -d\n  Then go to   http://yourserverip:3555  in your browser to access the login page.  More info on   Dockge and how to use it  And there you go — Docker and a tool to easily manage your containers are ready!   Watchtower , to auto-update containers   Watchtower is a container that checks for updates and pulls new images automatically, just by adding a label in your containers’   compose.yml  files.  Configuration   Open Dockge in your browser  Click   compose  Name the stack   watchtower  Paste the config below into the default config area in Dockge     ---\n   services  :\n     watchtower  :\n       container_name  :   watchtower\n       image  :   ghcr.io/nicholas-fedor/watchtower:latest\n       restart  :   unless-stopped\n       env_file  :\n         -   .env\n       environment  :\n         -   TZ=Europe/Paris\n         -   WATCHTOWER_SCHEDULE=${SCHEDULE}\n         -   WATCHTOWER_LABEL_ENABLE=true\n         -   WATCHTOWER_CLEANUP=true\n         -   WATCHTOWER_REMOVE_VOLUMES=true\n         # Discord notifications - uncomment if used\n         #- WATCHTOWER_NOTIFICATIONS=slack\n         #- WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER=Watchtower\n         #- WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=${WH_URL}\n       volumes  :\n         -   /var/run/docker.sock:/var/run/docker.sock\n  Then fill in the   .env  section in Dockge with the following:     SCHEDULE  =\n   WH_URL  =\n     Property  Value  Examples     SCHEDULE  Cron format   0 0 6 * * *  (every day at 6 AM)    WH_URL  Your Discord webhook URL - append   /slack  at the end   https://yourdiscordserver/webhook/slack  To have Watchtower monitor your other containers, add this to their   compose.yml :     labels  :\n     -   com.centurylinklabs.watchtower.enable=true\n  Then restart the modified stacks. And that's it — you now have a solid base to start deploying the services you want!  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":120,"path":121,"dir":103,"title":122,"description":7,"keywords":123,"body":128},"content:3.serveex:2.core:3.swag.md","/serveex/core/swag","SWAG",[106,124,125,126,127],"Dashboard","DBIP","Exposing Dockge","Exposing Another Service with SWAG","   SWAG   🎯    Objectives:   Install Swag  Enable SSL  Access the dashboard  Configure regional blocking  Expose Dockge   Swag  is the core of this homelab. It’s a powerful reverse proxy that allows you to expose services on the internet using domain names, handling SSL certificate issuance (for encrypted connections), request routing, and access security (via HTTP auth or SSO like Authelia or Authentik). All the necessary documentation is   available here .     SWAG is only useful for exposing your services to the internet—i.e., accessing them via a public URL like   https://service.mydomain.com . If you don’t want to expose your services and prefer to always use a VPN to connect remotely, you can go   here instead .  Below is an example exposing Dockge. We will install SWAG along with the dbip mod for geolocation-based blocking, and the dashboard mod for managing swag, fail2ban, and geolocation.   Reverse proxy principle and its application in our case:    Installation      This tutorial assumes you have a domain name pointing to your server, and that your router has a NAT rule forwarding port   443  to your server's IP and port   443 . The example domain will be   mydomain.com .  File structure to be modified:     root\n   └──   docker\n       └──   swag\n           ├──   config\n           │     ├──   dns-conf\n           │     │     └──   ovh.ini\n           │     └──   nginx\n           │         ├──   dbip.conf\n           │         ├──   nginx.conf\n           │         └──   proxy-confs\n           │             └──   dockge.subdomain.conf        \n           ├──   compose.yml\n           └──   .env\n  Open Dockge in your browser, click on   compose , name the stack   swag , and copy the following config:     ---\n   services  :\n     swag  :\n       image  :   lscr.io/linuxserver/swag:latest\n       container_name  :   swag\n       cap_add  :\n         -   NET_ADMIN\n       env_file  :\n         -   .env\n       environment  :\n         -   TZ=Europe/Paris\n         -   URL=${DOMAIN}\n         -   EXTRA_DOMAINS=${DOMAINS}\n         -   SUBDOMAINS=wildcard\n         -   VALIDATION=dns\n         -   DNSPLUGIN=${PLUGIN}\n         -   EMAIL=${EMAIL}\n         -   DOCKER_MODS=linuxserver/mods:swag-dbip|linuxserver/mods:swag-dashboard|linuxserver/mods:swag-auto-reload\n       volumes  :\n         -   /docker/swag/config:/config\n       ports  :\n         -   80:80\n         -   443:443\n         -   81:81\n       restart  :   unless-stopped\n       networks  :\n         -   swag\n   \n   networks  :\n     swag  :\n       name  :   swag_default\n   ✨   Tip: \nAdd the watchtower label to each container to enable automatic updates     services  :\n     swag  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Then in the   .env  file:     DOMAIN  =\n   DOMAINS  =\n   EMAIL  =\n   PLUGIN  =\n  Fill out the variables as follows:     Property  Value  Examples     DOMAIN  Your domain (covers all subdomains too)   mydomain.com    DOMAINS  Any additional domains   myseconddomain.com    EMAIL  Your email for generating the certificate   your@email.com    PLUGIN  Plugin for certificate generation—depends on your   DNS provider   ovh   cloudflare  Assuming your DNS zone is managed by OVH, deploy the stack once. The logs will show a failure in creating the SSL certificate due to a missing   ovh.ini  configuration. Stop the stack.  In CLI, go to the dns-conf folder and edit the   ovh.ini  file:   ✨   Tip for terminal-shy users: \nYou can use   File Browser  to browse and edit files instead of using terminal commands.     sudo   vi   /docker/swag/config/dns-conf/ovh.ini\n  You should see:     # Instructions: https://github.com/certbot/certbot/blob/master/certbot-dns-ovh/certbot_dns_ovh/__init__.py#L20\n   # Replace with your values\n   dns_ovh_endpoint   = ovh-eu\n   dns_ovh_application_key   = \n   dns_ovh_application_secret   = \n   dns_ovh_consumer_key   =\n  Authenticate and create   your token here .  Set the following permissions:    GET /domain/zone/*   PUT /domain/zone/*   POST /domain/zone/*   DELETE /domain/zone/*  Note the 3 keys temporarily and enter them in   ovh.ini . (In vim, press   i  to edit,   Esc  when done,   :x  to save and exit)  Save and exit the file.  Now configure swag to access DBIP, the geolocation-based access control module. Open the   nginx.conf  file:     sudo   vi   /docker/swag/config/nginx/nginx.conf\n  Add the following line below the   http  section:     include   /config/nginx/dbip.conf;\n  Restart the stack in Dockge. This time, the SSL certificate should be successfully generated! Check the logs to confirm the server is ready.  Dashboard   Access the dashboard locally by going to   http://yourserverip:81 \nOn the left, you'll see a list of currently \"proxied\" services (none yet). On the right, the list of banned IPs. Below, various indicators. For more details,   click here .    DBIP   DBIP allows you to block connections based on countries. It relies on the configuration file named   dbip.conf  located in   /docker/swag/config/nginx .   More info here .  In this example, we’ll configure it to block a list of countries known to be the source of most malicious traffic. We’ll also configure a variable to allow internal server traffic, your box’s local network, and a potential VPN in the 10.x.x.x range to access your services — but not the open internet.  This configuration can be enabled or disabled per service (see the Dockge example below).  Open   dbip.conf :     sudo   vi   /docker/swag/config/nginx/dbip.conf\n  Make your changes (  see documentation ), or use the following example:     geoip2   /config/geoip2db/dbip-country-lite.mmdb {\n       auto_reload   1w;\n       $  geoip2_data_continent_code     continent code;\n       $  geoip2_data_country_iso_code   country iso_code;\n   }\n   \n   # Country Codes: https://en.wikipedia.org/wiki/ISO_3166-2\n   \n   map   $geoip2_data_country_iso_code $geo-whitelist {\n       default   no;\n       FR   yes;\n   }\n   \n   map   $geoip2_data_country_iso_code $geo-blacklist {\n       default   yes;\n       CN   no;   #China\n       RU   no;   #Russia\n       HK   no;   #Hong Kong\n       IN   no;   #India\n       IR   no;   #Iran\n       VN   no;   #Vietnam\n       TR   no;   #Turkey\n       EG   no;   #Egypt\n       MX   no;   #Mexico\n       JP   no;   #Japan\n       KR   no;   #South Korea\n       KP   no;   #North Korea\n       PE   no;   #Peru\n       BR   no;   #Brazil\n       UA   no;   #Ukraine\n       ID   no;   #Indonesia\n       TH   no;   #Thailand\n   }\n   \n   geo   $lan-ip {\n       default   no;\n       10.0.0.0/  8   yes;\n       172.16.0.0/  12   yes;\n       192.168.0.0/  16   yes;\n       127.0.0.  1   yes;\n   }\n  Save and close the file. Restart the stack.  In the domain config files (see next section), you can enable or disable the whitelist or blacklist (  see documentation here ). In our case, the whitelist allows only French requests. The blacklist blocks only the listed countries. We'll use the blacklist, like so:     server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   some-app.*;\n       include   /config/nginx/ssl.conf;\n       client_max_body_size   0  ;\n   \n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       location   /   {\n  Exposing Dockge    📋   Prerequisite:     \nWe assume that you have created a subdomain like   dockge.mydomain.com  in your   DNS zone , with a   CNAME  pointing to   mydomain.com  and — unless you're using   Cloudflare Zero Trust  — that you've forwarded port   443  from your router to the server's   443  in   your NAT rules .  Now it's time to expose Dockge on the internet so you can access and manage your containers remotely. We assume you've set up the subdomain   dockge.mydomain.com  with a   CNAME  pointing to   mydomain.com .     Dockge does not support multi-factor authentication. Exposing it online could compromise all connected machines. Only do this if you're using an MFA solution like   Authentik . Otherwise, don’t expose it with SWAG — use a VPN like   Wireguard  instead.  Open the   dockge.subdomain.conf  file:     sudo   vi   /docker/swag/config/nginx/proxy-confs/dockge.subdomain.conf\n  Configure it like this:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n       \n       server_name   dockge.*;    # define the subdomain to redirect\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }   # all countries un blacklist are forbidden\n   \n       #include /config/nginx/ldap-server.conf;\n       #include /config/nginx/authelia-server.conf;\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           #include /config/nginx/ldap-location.conf;\n           #include /config/nginx/authelia-location.conf;\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           \n           set   $upstream_app dockge;   # container name\n           set   $upstream_port   5001  ;   # internal container port (not exposed port)\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Save and exit. The configuration will update within a few seconds.     By default, SWAG doesn’t recognize the name \"dockge\". You’ll need to add Dockge’s network to SWAG’s   compose.yml .  Go to the SWAG stack, click   edit , and modify the config file like this (note the   networks  section):     services  :\n     swag  :\n       container_name  :   #...\n       # ...\n       networks  :              # Link the container to the custom network\n         -   dockge             # Network name as defined in the stack\n   \n   networks  :                  # Define the custom network\n     # ...\n     dockge  :                  # Network name as defined in the stack\n       name  :   dockge_default   # True external network name\n       external  :   true\n     We assume the Dockge network is named   dockge_default . You can verify the setup works by checking the SWAG dashboard at   http://yourserverip:81 .  Redeploy the SWAG stack.  Wait a moment, then visit   https://dockge.mydomain.com  in your browser — you should be redirected to Dockge. You can also check the service status from the dashboard (  http://yourserverip:81  on your local network).  Exposing Another Service with SWAG   SWAG includes templates for most known services, named   servicename.subdomain.conf.sample . Just create the subdomain in your registrar's DNS zone (like OVH), point it to your main domain via a CNAME, then copy and rename the sample file:     cd   /docker/swag/config/proxy-confs\n   sudo   cp   servicename.subdomain.conf.sample   servicename.subdomain.conf\n      If the subdomain is not redirected properly   Open the file and verify the container name in    set   $upstream_app containername;  Make sure you added the container's network in SWAG’s   compose.yml  You can also customize the subdomain by editing    server_name   yoursubdomain.*;  and renaming the file to   yoursubdomain.subdomain.conf .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":130,"path":131,"dir":132,"title":133,"description":7,"keywords":134,"body":138},"content:3.serveex:3.security:1.wireguard.md","/serveex/security/wireguard","security","Wireguard",[34,135,136,137],"Server Setup","Client Server Setup","Other Devices","   Wireguard   🎯   Goals:   Install Wireguard  Configure clients  Access the secure network  Introduction   Using a VPN allows remote access to a server’s local resources without exposing them to the internet. It’s a clean and secure way to access services like SSH without exposing the port publicly. With a VPN, you can securely connect to your network from anywhere and make devices on different networks communicate.  Here we will use   Wireguard , a secure and high-performance VPN server, using containers:    wg-easy  as the server, providing a very simple web UI to manage connections and download config files (including QR codes for phones)   Wireguard  as the client for Linux systems  Clients are also available for Windows, macOS, iOS, and Android.  The concept:   On the internet, anyone can reach any internet box and thus any exposed server.  Your server is on your local network. It is accessible only locally unless services are explicitly exposed (as we did with Dockge). To access non-exposed resources, you must be on the same local network.  We want to securely access these unexposed services (like SSH) from anywhere.  We also want to connect services between servers, like linking two Dockge instances securely.  To achieve this, we’ll create a   Virtual Private Network  (VPN), i.e., a secure tunnel that only connected machines can use. They’ll appear to be on the same private network.  Additionally, you can add your phone, laptop, or other devices to the VPN and securely access your server resources wherever you are.    In this diagram, machine 1 is part of two networks:   Its local network (devices behind the same router, e.g.   192.168.x.x  – machines 1 and 2)  The VPN network (VPN devices with a second IP, e.g.   10.8.x.x  – machines 1 and 4)  You   can  allow VPN clients to share access to their local networks, but we won’t do that here for security and subnet conflict reasons (e.g., if two remote machines use the same local IP like   192.168.1.1 ).  So only VPN-connected devices can communicate with each other on the VPN, not with other local devices outside the VPN.  Server Setup    📋   Pre-flight Checklist:   Ensure port   51820 UDP  is free on your server and correctly forwarded from your router (  51820 UDP -> Server ).  Ensure port   51821 TCP  is free for the web UI.      Warning : If your IP is not static, use a Dynamic DNS service (  DynDNS ). If your ISP uses   CGNAT , you’ll need to use an external VPS and connect your local server as a client.  Folder Structure     root\n   └──   docker\n       └──   wg-easy\n           ├──   config\n           │     └──   etc_wireguard\n           ├──   compose.yaml\n           └──   .env\n  Open Dockge, click   Compose , and name the stack   wg_easy .  Copy the following configuration:     ---\n   services  :\n     wg-easy  :\n       environment  :\n         -   INSECURE=true\n       image  :   ghcr.io/wg-easy/wg-easy:15\n       container_name  :   wg-easy\n       networks  :\n         wg  :\n           ipv4_address  :   10.42.42.42\n           ipv6_address  :   fdcc:ad94:bacf:61a3::2a\n       volumes  :\n         -   ./etc_wireguard:/etc/wireguard\n         -   /lib/modules:/lib/modules:ro\n       ports  :\n         -   \"51820:51820/udp\"\n         -   \"51821:51821/tcp\"\n       restart  :   unless-stopped\n       cap_add  :\n         -   NET_ADMIN\n         -   SYS_MODULE\n       sysctls  :\n         -   net.ipv4.ip_forward=1\n         -   net.ipv4.conf.all.src_valid_mark=1\n         -   net.ipv6.conf.all.disable_ipv6=0\n         -   net.ipv6.conf.all.forwarding=1\n         -   net.ipv6.conf.default.forwarding=1\n   \n   networks  :\n     wg  :\n       driver  :   bridge\n       enable_ipv6  :   true\n       ipam  :\n         driver  :   default\n         config  :\n           -   subnet  :   10.42.42.0/24\n           -   subnet  :   fdcc:ad94:bacf:61a3::/64\n   ✨   Tip:   You can customize WireGuard and web UI ports.  Add a Watchtower label for automatic updates:     services  :\n     wg-easy  :\n       # ...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Deploy the stack and access the local web UI at   http://server-ip:51821 .     If the deployment fails, check your firewall rules.  Once connected, follow the web UI instructions to:   Create your admin account and password.  Set the host field (use your public IP or domain name).  Then go to   Administrator → Admin Panel → Config :   Change   Allowed IPs  from   0.0.0.0/24  to   10.8.0.0/24  for   split tunneling .  Remove IPv6 (it often causes unnecessary issues).  Retrieve Configuration Files  To configure clients:   Access the web UI:   http://server-ip:51821  Create a new client  Edit the client and add   10.8.0.0/24  to   Server Allowed IPs  (Optional) Set   Persistent Keep Alive  to   25  if it’s a permanently connected client  Save, download, and rename the file to   wg0.conf  (or   wg1.conf , etc.)  Client Server Setup      We assume the client server runs Linux with Docker installed.  Folder Structure     root\n   └──   docker\n       └──   wireguard\n           └──   config\n           │     └──   wg_confs\n           └──   compose.yaml\n  Create the folder:     sudo   mkdir   -p   /docker/wireguard/config/wg_confs\n   ✨   Tip:  You can use   File Browser  instead of the terminal to edit and upload files.  Create the   wg0.conf  file:     sudo   vi   /docker/wireguard/config/wg_confs/wg0.conf\n  Enter insert mode (  i ), paste the downloaded configuration, then save (  Esc  →   :x ).   ✨   Alternative method:  Transfer the file via SFTP and move it:     sudo   cp   ~/wg0.conf   /docker/wireguard/config/wg_confs\n  Create the   compose.yaml  file in   /docker/wireguard :     services  :\n     wireguard  :\n       image  :   lscr.io/linuxserver/wireguard:latest\n       container_name  :   wireguard\n       network_mode  :   host\n       cap_add  :\n         -   NET_ADMIN\n         -   SYS_MODULE\n       environment  :\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/wireguard/config:/config\n         -   /lib/modules:/lib/modules\n       restart  :   unless-stopped\n  Start the container:     cd   /docker/wireguard\n   sudo   docker   compose   up   -d\n     Repeat this setup for each client.  Other Devices     Mobile:  Install WireGuard and scan the QR code via the web UI (  http://server-ip:51821 )   Desktop:  Install the WireGuard client and import the downloaded config file.      Note:  If the client machine is on the same local network as the server, edit the   wg0.conf  file to use the local server IP:\n  Endpoint = server-local-ip:51820  And here’s the final setup overview:    .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":140,"path":141,"dir":132,"title":142,"description":7,"keywords":143,"body":150},"content:3.serveex:3.security:2.authentik.md","/serveex/security/authentik","Authentik",[106,144,145,146,147,148,149],"Exposing Authentik","Enable Multifactor Authentication","Protecting a Native App","Protecting an App via Reverse Proxy","Protecting a Remote Server Service","Migrating an Authentik Database","   Authentik   🎯   Objectives:   Install and expose Authentik  Configure Multi-Factor Authentication (MFA)  Protect a native app or an app behind a reverse proxy   Authentik  is a single sign-on (SSO) tool that allows you to log in once to all platforms compatible with OpenID. It can also secure access to your exposed services by injecting itself via SWAG into requests to those services.  For example, if you're exposing Dockge online at   dockge.mydomain.com , you’ll first land on an Authentik login page when accessing it. If you've already authenticated with another Authentik-protected service, you won’t need to log in again. This allows you to authenticate only once per day for all protected services.  Authentik also supports multi-factor authentication, including TOTP (a code generated by the authentication app of your choice). Additionally, it allows login through Microsoft or Google accounts, provided you've configured one of those applications.  It's a great alternative to VPNs for securely exposing services, especially ones that lack MFA or login protection (e.g., the SWAG dashboard).  Authentik has   extensive documentation  and   great tutorials from Cooptonian . Here, we’ll cover the basics using Dockge as an example.  There are two main modes you should know:   The first allows apps with native support for OpenID-compatible SSO to connect directly to Authentik. This is the preferred method, as the app itself decides what’s public and what’s protected.     The second method injects Authentik authentication through SWAG before reaching the target service.    Both modes can be configured on a per-application basis.  Installation   Folder structure:     root\n   └──   docker\n       └──   authentik\n           ├──   .env\n           ├──   compose.yml\n           ├──   media\n           ├──   certs\n           ├──   custom-template\n           └──   ssh\n  Create the folders:     sudo   mkdir   -p   /docker/authentik/media   /docker/authentik/certs   /docker/authentik/custom-template   /docker/authentik/ssh\n  Navigate to the   authentik  folder via   cd /docker/authentik  and generate a password and secret key to include in the   .env  file:     sudo   echo   \"PG_PASS=$(  openssl   rand   36   |   base64  )\"   >>   .env\n   sudo   echo   \"AUTHENTIK_SECRET_KEY=$(  openssl   rand   60   |   base64  )\"   >>   .env\n     To generate the keys, we created the folders ahead of deployment using Dockge. Dockge will prevent you from creating a stack with the same name in these folders unless a   compose.yml  file exists. So, create an empty   compose.yml  so it appears as an inactive stack:     sudo   vi   /docker/authentik/compose.yml\n  Open Dockge and search for \"authentik\" in the inactive stacks.\nName the stack   authentik  and paste the following configuration, replacing    {AUTHENTIK_TAG:-2026.2}  with   the latest version of Authentik .     ---\n   services  :\n   \n     postgresql  :\n       image  :   docker.io/library/postgres:16-alpine\n       container_name  :   authentik-postgresql\n       restart  :   unless-stopped\n       healthcheck  :\n         test  :\n           -   CMD-SHELL\n           -   pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}\n         start_period  :   20s\n         interval  :   30s\n         retries  :   5\n         timeout  :   5s\n       volumes  :\n         -   database:/var/lib/postgresql/data\n       environment  :\n         POSTGRES_PASSWORD  :   ${PG_PASS:?database password required}\n         POSTGRES_USER  :   ${PG_USER:-authentik}\n         POSTGRES_DB  :   ${PG_DB:-authentik}\n       env_file  :\n         -   .env\n   \n     redis  :\n       image  :   docker.io/library/redis:alpine\n       container_name  :   authentik-redis\n       command  :   --save 60 1 --loglevel warning\n       restart  :   unless-stopped\n       healthcheck  :\n         test  :\n           -   CMD-SHELL\n           -   redis-cli ping | grep PONG\n         start_period  :   20s\n         interval  :   30s\n         retries  :   5\n         timeout  :   3s\n       volumes  :\n         -   redis:/data\n     \n     server  :\n       image  :   ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2}\n       container_name  :   authentik-server\n       restart  :   unless-stopped\n       command  :   server\n       environment  :\n         AUTHENTIK_REDIS__HOST  :   redis\n         AUTHENTIK_POSTGRESQL__HOST  :   postgresql\n         AUTHENTIK_POSTGRESQL__USER  :   ${PG_USER:-authentik}\n         AUTHENTIK_POSTGRESQL__NAME  :   ${PG_DB:-authentik}\n         AUTHENTIK_POSTGRESQL__PASSWORD  :   ${PG_PASS}\n       volumes  :\n         -   ./media:/media\n         -   ./custom-templates:/templates\n         -   ./ssh:/authentik/.ssh\n       env_file  :\n         -   .env\n       ports  :\n         -   ${COMPOSE_PORT_HTTP:-9000}:9000\n         -   ${COMPOSE_PORT_HTTPS:-9443}:9443\n       depends_on  :\n         -   postgresql\n         -   redis\n   \n     worker  :\n       image  :   ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2}\n       container_name  :   authentik-worker\n       restart  :   unless-stopped\n       command  :   worker\n       environment  :\n         AUTHENTIK_REDIS__HOST  :   redis\n         AUTHENTIK_POSTGRESQL__HOST  :   postgresql\n         AUTHENTIK_POSTGRESQL__USER  :   ${PG_USER:-authentik}\n         AUTHENTIK_POSTGRESQL__NAME  :   ${PG_DB:-authentik}\n         AUTHENTIK_POSTGRESQL__PASSWORD  :   ${PG_PASS}\n       # `user: root` and the docker socket volume are optional.\n       # See more for the docker socket integration here:\n       # https://goauthentik.io/docs/outposts/integrations/docker\n       # Removing `user: root` also prevents the worker from fixing the permissions\n       # on the mounted folders, so when removing this make sure the folders have the correct UID/GID\n       # (1000:1000 by default)\n       user  :   root\n       volumes  :\n         -   /var/run/docker.sock:/var/run/docker.sock\n         -   ./media:/media\n         -   ./certs:/certs\n         -   ./custom-templates:/templates\n         -   ./ssh:/authentik/.ssh\n       env_file  :\n         -   .env\n       depends_on  :\n         -   postgresql\n         -   redis\n   \n   volumes  :\n     database  :\n       driver  :   local\n     redis  :\n       driver  :   local\n  In the   .env  file, the   PG_PASS  and   AUTHENTIK_SECRET_KEY  variables are already set.\nDeploy the stack.  You can then begin the initial setup by visiting:   http://yourserverip:9000/if/flow/initial-setup/      Warning:  It’s recommended to create a new admin account and   disable  the default   akadmin  account.  Exposing Authentik   To use Authentik outside your local network, you must expose it.   📋   Prerequisites:      \nWe assume you have already created a subdomain like   auth.mydomain.com  in your   DNS zone , with a CNAME pointing to   mydomain.com . Also, unless you're using   Cloudflare Zero Trust , you must have already forwarded port   443  from your router to port   443  of your server in your   NAT rules .  Open the   authentik-server.conf  file:   ✨   Tip for those who dislike terminals: \nYou can use   File Browser  to navigate and edit files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/authentik-server.conf\n  Verify that the following variables are set correctly:     set   $upstream_authentik authentik-server;\n   proxy_pass   http://$upstream_authentik:9000;\n  If not, press   i  to enter edit mode, make the necessary changes, then save and exit by pressing   Esc  followed by   :x .  Create the   auth.subdomain.conf  file:     sudo   vi   /docker/swag/config/nginx/proxy-confs/auth.subdomain.conf\n  Press   i  to enter edit mode and paste the following configuration:     ## Version 2023/05/31\n   # Ensure your authentik container is named authentik-server\n   # Ensure your DNS has a CNAME for authentik\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   auth.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       location   /   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app authentik-server;\n           set   $upstream_port   9000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   \n       location   ~   (/authentik)?/api   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app authentik-server;\n           set   $upstream_port   9000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Save and exit by pressing   Esc  then   :x .  Go to Dockge, and edit the SWAG compose file to add the Authentik network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ...\n        networks  :                 # Attach the container to the custom network\n         # ...           \n         -   authentik              # Name of the network declared in the stack\n   \n   networks  :                      # Define the custom network\n     # ...\n     authentik  :                   # Name of the network declared in the stack\n       name  :   authentik_default    # Actual name of the external network\n       external  :   true             # Indicates it's an external network\n  Restart the stack and wait for SWAG to be fully operational.  Done! You can now access Authentik via   https://auth.mydomain.com  Enable Multifactor Authentication   The main value of Authentik is using multifactor authentication for all protected apps.   Go to   https://auth.mydomain.com  Log in  Go to   Settings  Click the   MFA  section  Click   Register  Choose a method like   TOTP device  (you'll need an authenticator app like Google Authenticator)  Follow the steps  You’ll now be prompted to enter a one-time code at every login.  Protecting a Native App   Authentik is natively compatible with several applications. You can find the list and   support here .  Protecting an App via Reverse Proxy   SWAG lets you insert Authentik’s login page between a request and access to your service. To do this:   Configure the authentication provider in Authentik.  Edit the domain proxy file so SWAG can intercept the request.  Why do this when Dockge already has authentication? Because Dockge uses weak HTTP authentication. With Authentik, you get strong MFA authentication and automatic login to all apps protected by Authentik. This secures access to Dockge and other apps without needing a VPN.  Configuring Authentik   Go to Authentik  Open the admin panel  Select   Applications  then   Create with wizard  Fill in the fields as shown:     At the next step, choose \"Forward authentication (single application)\" and configure it as shown (flows are important):     Next, go to the   Outposts  menu on the left and edit   authentik Embedded Outpost :     Add the   dockge  application by moving it to the right column and save.  Configuring SWAG  Edit the file   dockge.mydomain.com :     sudo   vi   /docker/swag/config/nginx/proxy-confs/dockge.subdomain.conf\n  Press   i  to enter edit mode and uncomment the two lines   #include /config/nginx/authentik-server.conf;  Press   Esc , type   :x , and press   Enter  to save and exit.  Done! Now when accessing   https://dockge.mydomain.com , you’ll be redirected to the Authentik login screen.   ✨   Tip:  In Dockge's settings, you can disable Dockge's authentication to avoid double login.   Warning : this means if the port is open on your local network, there will be no authentication at all.     Repeat this process for each app you want to protect (unless it has native integration with Authentik).  Your new architecture looks like this:    Protecting a Remote Server Service   For a   native application  (via OAuth 2.0 or other), nothing changes.  For a non-native app behind a reverse proxy, you must deploy an   Outpost . An Outpost is a container acting as a local proxy — it's the target of your app's auth requests and the only one authorized to communicate with your Authentik API.   Prerequisites:   Install   Docker  on the remote server hosting the service.  If the app has no native integration, use a compatible reverse proxy. We will use   SWAG  here.  This container will forward requests to your main   Authentik  instance over the internet (or your local network). The server will perform checks and respond to the Outpost, which will allow or block access accordingly.    Configuring Authentik  Create your   providers and applications  as shown earlier.  Then, in the admin panel, go to   Applications > Outposts , and create a new outpost.  Fill in as follows:     Field  Value     Name  Your preferred name    Type   Proxy    Integration  Leave empty    Applications  Select the applications you previously created  In the   Advanced settings  section, clear the existing content and enter:     log_level  :   info\n   docker_labels  :   null\n   authentik_host  :   https://your_authentik_server_domain/\n   object_naming_template  :   ak-outpost-%(name)s\n   authentik_host_insecure  :   false\n   container_image  :\n   docker_network  :   null\n   docker_map_ports  :   true\n   docker_labels  :   null\n  Save and exit.  On the list of created outposts, locate the new one and click   Show details  at the end of the line. Carefully copy the access token.  Configuring the Remote Machine  We assume you’ve already installed   Docker  and   SWAG  on this remote machine.  On your remote machine, use   Dockge  to create a stack named   authentik-outpost .  If you haven’t installed   Dockge , create a folder   /docker/authentik-outpost , or directly via command line:     sudo   mkdir   -P   /docker/authentik-outpost\n   ✨   Tip for terminal-averse users: \nYou can use   File Browser  to navigate and edit your files instead of using terminal commands.  Create the   compose.yaml  file or paste the configuration directly into Dockge if installed.  Via command line:     sudo   vi   /docker/authentik-outpost/compose.yaml\n  Enter edit mode by pressing   i  and paste the following configuration, updating the version in    {AUTHENTIK_TAG:proxy:2024.2.3}  to match your Authentik server version.     version  :   \"3.5\"\n   services  :\n     authentik_proxy  :\n       container_name  :   authentik-outpost\n       image  :   ghcr.io/goauthentik/proxy:2024.2.3\n       # Optionally specify which networks the container should be\n       # might be needed to reach the core authentik server\n       restart  :   unless-stopped\n       env_file  :\n         -   .env\n       ports  :\n         -   9000:9000\n         -   9443:9443\n       environment  :\n         AUTHENTIK_HOST  :   ${HOST}\n         AUTHENTIK_INSECURE  :   \"false\"\n         AUTHENTIK_TOKEN  :   ${TOKEN}\n  Go to the SWAG stack on the remote machine (or edit directly using Dockge) and add the authentik-outpost network in the configuration file like this (see   networks  section):     sudo   vi   /docker/swag/compose.yaml\n     services  :\n     swag  :\n        container_name  :   #...\n         # ... \n        networks  :                        # Attach the container to the custom network\n         -   authentik-outpost             # Network name as declared in the stack\n   \n   networks  :                             # Define the custom network\n     #...\n     authentik-outpost  :                  # Name of the network declared in the stack\n       name  :   authentik-outpost_default   # Actual name of the external network\n       external  :   true                    # Marks it as an external network\n  Press   Esc , then type   :x  and press   Enter  to save and exit.     We assume the Dockge network name is   authentik-outpost_default .  If using   Dockge , restart SWAG.  Otherwise, via terminal:     cd   /docker/swag/\n   sudo   docker   compose   up   -d\n  Create (or fill using Dockge) the   .env  file in the   authentik-outpost  directory:  Via command line:     sudo   vi   /docker/authentik-outpost/.env\n  Enter edit mode with   i  and paste the following configuration:     HOST  =\n   TOKEN  =\n  Fill in the values:     Variable  Value  Example      HOST  The URL of your Authentik server   https://auth.domain.com     TOKEN  The previously copied access token   Q2pVEqsTNRkJSO9SkJzU3KZ2  Press   Esc , then type   :x  and press   Enter  to save and exit.  If using Dockge, deploy the stack.  Otherwise, via terminal:     cd   /docker/authentik-outpost/\n   sudo   docker   compose   up   -d\n  The container is now running. You can verify its status from your Authentik instance admin panel under   Applications > Outposts .  Now, let’s configure SWAG.  Open the   authentik-server.conf  file:     sudo   vi   /docker/swag/config/nginx/authentik-server.conf\n  In the file, press   i  to enter edit mode and change   authentik-server  to   authentik-outpost  as shown:     set   $upstream_authentik authentik-outpost;\n   proxy_pass   http://$upstream_authentik:9000;\n  Save and exit with   Esc , then   :x  and   Enter .  Then configure the applications to protect as you did on your main server, whether they are   native  or protected via   reverse proxy .  Migrating an Authentik Database   On the source machine, dump the database:     sudo   docker   exec   authentik-postgres   pg_dump   -U   authentik   -F   t   authentik   >   /path/to/mydb.tar\n  Then transfer it to the target machine. On the target machine, copy the file into the Docker container:     cp   /path/to/mydb.tar   authentik-postgres:/path/to/wherever\n  (Optional) Purge existing tables:     sudo   docker   exec   -i   authentik-postgres   psql   -U   authentik   -c   \"SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'authentik' AND pid \u003C> pg_backend_pid();\"   &&   sudo   docker   exec   -i   authentik-postgres   psql   -U   authentik   -d   postgres   -c   \"DROP DATABASE IF EXISTS authentik;\"   &&   sudo   docker   exec   -i   authentik-postgres   psql   -U   authentik   -d   postgres   -c   \"CREATE DATABASE authentik;\"\n  Restore the database:     sudo   docker   exec   authentik-postgresql   pg_restore   -U   authentik   -d   authentik   /path/to/wherever/mydb.tar\n  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":152,"path":153,"dir":132,"title":154,"description":7,"keywords":155,"body":159},"content:3.serveex:3.security:3.cloudflare.md","/serveex/security/cloudflare","Cloudflare Zero Trust",[34,156,157,158],"Cloudflare Configuration","SWAG Configuration","Managing Multiple Tunnels for Multiple Servers","   Cloudflare Zero Trust   🎯   Goals:   Understand the concept of Cloudflare Tunnels  Configure your Cloudflare account  Configure SWAG  Manage multiple tunnels    Introduction   The   Zero Trust  architecture is the practice of designing systems based on the principle of   \"never trust, always verify\" , as opposed to the traditional principle of   \"trust, but verify\" . This concept has become increasingly popular recently due to the growing number of attacks targeting user data. It’s a broad concept, but we’ll focus on how to apply   Zero Trust  to the web services we host.   Cloudflare tunnels  offer a simple way to implement   Zero Trust , using   SWAG  and   Authentik .  Simply put, Cloudflare Tunnels allow you to:   Hide your server’s IP (and your home IP if it's self-hosted)  Authenticate traffic  Benefit from Cloudflare protections (DDoS attacks, blacklists, malicious requests, etc.)  Use Cloudflare's CDN to cache and speed up your websites  Avoid opening router ports for services exposed by SWAG  Here we’ll explain how to integrate SWAG with Cloudflare tunnels.      Warning:   Do not use Cloudflare tunnels to expose a mail server  Do not use Cloudflare tunnels to expose a video service like Plex (if you followed   this guide , Plex is not exposed, so it’s fine)  Do not use Cloudflare tunnels for the BitTorrent protocol (if you followed   this guide , everything is fine)  Cloudflare Configuration   DNS Zone  First, you need to set Cloudflare as your   DNS zone  manager. If you bought your domain from Cloudflare, that’s already done. Otherwise, check with your registrar how to add external DNS servers. Cloudflare provides   step-by-step documentation  on how to configure a DNS Zone, whether your domain is external or registered with Cloudflare.  If you only have one server to protect behind Cloudflare, you can delete all existing DNS records. By default, your domain and all its subdomains will be redirected to the tunnel.  If you have subdomains pointing to other servers, you can still define them in the DNS zone using A records.  If you have several servers and tunnels under one domain,   see here .  API Key  Start by creating a new Cloudflare API token and retrieving your zone and account IDs.  On your Cloudflare dashboard, on your domain overview page, you’ll see the   zone  and   account  IDs at the bottom right. Save both securely.    Just below that is a link titled   Get your API token . Click it. The token scope must include   Zone:DNS:Edit  and   Account:Cloudflare Tunnel:Edit . Your page should look like the screenshot below.    Once created, your token will only be shown once. Save it securely, as it cannot be viewed again later.  Cloudflare Zero Trust  You must register for   Cloudflare Teams  to access the   Zero Trust  dashboard that manages tunnels and access policies. This is a premium service, but there’s a free plan for up to 50 users—perfect for a home lab. Keep in mind that a valid credit card is required to register, but the free plan incurs no charges.  Register   via this link .  SWAG Configuration      This guide assumes you own   mondomaine.fr  and that its DNS is correctly pointing to Cloudflare, as described above.  SWAG supports two Docker Mods:    Cloudflared , the container used to create and manage tunnels   Cloudflared Real IP , which allows SWAG to receive the true source IP of incoming requests instead of Docker’s internal IP (important for IP geolocation mods like DBIP).  These two mods, merged into the SWAG container, require some configuration.  Tunnel Configuration  Create a file   tunnelconfig.yml  to reference in your SWAG   compose.yaml .   ✨   Tip:  Use   File Browser  to navigate and edit files instead of using the terminal.     sudo   vi   /docker/swag/config/tunnelconfig.yml\n  Press   i  to enter insert mode and paste:     ingress  :\n     -   hostname  :   mondomaine.fr\n       service  :   https://mondomaine.fr\n     -   hostname  :   \"*.mondomaine.fr\"\n       service  :   https://mondomaine.fr\n     -   service  :   http_status:404\n  Press   Esc , then save and exit with   :x  and   Enter .  Cloudflare Real IP Configuration  Now configure   Cloudflare Real IP .  Open the   nginx.conf  file:     sudo   vi   /docker/swag/config/nginx/nginx.conf\n  Press   i  and add the following at the end of the   http  section:     real_ip_header   X-Forwarded-For;\n   real_ip_recursive   on  ;\n   include   /config/nginx/cf_real-ip.conf;\n   set_real_ip_from   127.0.0.1  ;\n  Save and exit with   :x .  Docker Compose  In Dockge, edit your SWAG stack with this:     ---\n   services  :\n     swag  :\n       image  :   lscr.io/linuxserver/swag:latest\n       container_name  :   swag\n       cap_add  :\n         -   NET_ADMIN\n       env_file  :\n         -   .env\n       environment  :\n         -   DOCKER_MODS=linuxserver/mods:swag-dbip|linuxserver/mods:swag-dashboard|linuxserver/mods:swag-auto-reload|linuxserver/mods:universal-cloudflared|linuxserver/mods:swag-cloudflare-real-ip\n         -   PUID=${PUID}\n         -   PGID=${PGID}\n         -   TZ=Europe/Paris\n         -   URL=${DOMAIN}\n         -   SUBDOMAINS=wildcard\n         -   VALIDATION=dns\n         -   DNSPLUGIN=${PLUGIN}\n         -   EMAIL=${EMAIL}\n         -   CF_ZONE_ID=${ZONE_ID}\n         -   CF_ACCOUNT_ID=${ACCOUNT_ID}\n         -   CF_API_TOKEN=${API_TOKEN}\n         -   CF_TUNNEL_NAME=${TUNNEL_NAME}\n         -   CF_TUNNEL_PASSWORD=${TUNNEL_PW}\n         -   FILE__CF_TUNNEL_CONFIG=/config/tunnelconfig.yml\n       extra_hosts  :\n         -   ${DOMAIN}:127.0.0.1\n       ports  :\n         -   81:81\n       volumes  :\n         -   /docker/swag/config:/config\n         -   /docker/swag/config/fail2ban/fail2ban.sqlite3:/dashboard/fail2ban.sqlite3:ro\n       restart  :   unless-stopped\n   ✨   Tip:  Add a Watchtower label to automate updates:     labels  :\n     -   com.centurylinklabs.watchtower.enable=true\n  Fill in your   .env  file:     PUID  =\n   PGID  =\n   DOMAIN  =\n   PLUGIN  =\n   EMAIL  =\n   ZONE_ID  =\n   ACCOUNT_ID  =\n   API_TOKEN  =\n   TUNNEL_NAME  =\n   TUNNEL_PW  =\n     Variable  Value  Example     PUID  User ID (  id username )   1000    GUID  Group ID (  id username )   1000    DOMAIN  Your reserved domain   mondomaine.fr    PLUGIN  DNS provider (also configure   cloudflare.ini )   cloudflare    EMAIL  Email for the certificate   you@email.com    ZONE_ID  Cloudflare Zone ID   aNhcz1l3JfWbFZo2XMpzQlP2iOqk    ACCOUNT_ID  Cloudflare Account ID   buKsjNHLyzKMM1qYnzOy4s7SHfly    API_TOKEN  API token   53ydYus9TFFk1DOXNdP87iIcJtQjoW    TUNNEL_NAME  Tunnel name   my_tunnel    TUNNEL_PW  Strong, random password   iSzKRmP4VbnlsMvdSdgBEJiJi  Once done, deploy the stack. Check the logs—you should reach   server ready .  Then confirm your tunnel appears under   Networks > Tunnels  in   Cloudflare Zero Trust . By default, all subdomains will be routed through the tunnel—no need to define them   in your DNS zone .   ✨   Tip:  If you want to expose a service without a tunnel, just define an A record   in your DNS zone . If resolution fails, disable the proxy function for that record—e.g., for   sub.mondomaine.fr .\n   Managing Multiple Tunnels for Multiple Servers   By default, all subdomains of your domain are routed through the single tunnel. But if you have a second server, just change the tunnel name in that SWAG instance.  In your DNS zone, redirect subdomains to the correct tunnel.  Go to   Networks > Tunnels  in   Cloudflare Zero Trust .  Note the tunnel IDs:    Then in the   Cloudflare DNS dashboard , click your domain name.  Click   Add Record  and add these two CNAME records (include   .cfargotunnel.com ):     Type  Name  Target     CNAME   subdomain1   yourtunnelid1.cfargotunnel.com    CNAME   subdomain2   yourtunnelid2.cfargotunnel.com  If you have many subdomains, point them to the above reference subdomains.  This way, if a tunnel ID changes, you only update one DNS record.  Example:    sub1  and   sub2  also point to the server behind   subdomain1 :     Type  Name  Target     CNAME   sub1   subdomain1    CNAME   sub2   subdomain1    sub3  and   sub4  point to the server behind   subdomain2 :     Type  Name  Target     CNAME   sub3   subdomain2    CNAME   sub4   subdomain2  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":161,"path":162,"dir":163,"title":164,"description":7,"keywords":165,"body":167},"content:3.serveex:4.monitoring:1.uptime-kuma.md","/serveex/monitoring/uptime-kuma","monitoring","Uptime-Kuma",[106,166],"Expose with Swag","   Uptime-Kuma   🎯   Goals:   Install and deploy Uptime-Kuma  Expose Uptime-Kuma  (Optional) Protect Uptime-Kuma with Authentik   Uptime-Kuma  is a container dedicated to service monitoring. The principle is to regularly send requests to your services to determine if they are online, and alert you if not. Uptime-Kuma is developed by the same developer as Dockge.    Installation   Folder structure     root\n   └──   docker\n       └──   uptime-kuma\n           ├──   date\n           └──   compose.yaml\n  Open Dockge, click on   compose , name the stack   uptime-kuma , then copy and paste the following:     ---\n   services  :\n     uptime-kuma  :\n       image  :   louislam/uptime-kuma:2-slim\n       container_name  :   uptime-kuma\n       volumes  :\n         -   /docker/uptime-kuma/uptime-kuma-data:/app/data\n       ports  :\n         -   3200:3001   # \u003CHost Port>:\u003CContainer Port>\n       restart  :   always\n   ✨   Tip:  Add the Watchtower label to each container to automate updates     services  :\n     uptime-kuma  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  You can now access the tool via   http://yourserverip:3200 .      If it fails:  check your firewall rules.  Expose with Swag    📋   Before you begin:   \nWe assume you have the subdomain   stats.mydomain.com  with a   CNAME  pointing to   mydomain.com  in your   DNS zone . And of course,   unless you're using Cloudflare Zero Trust , port   443  of your router should point to port   443  of your server via   NAT rules .     Uptime-Kuma does not use multi-factor authentication. Exposing Uptime-Kuma on the internet could compromise the machines it monitors. Only do this if you're using an MFA system like   Authentik . Otherwise, don’t expose it with SWAG; use a VPN like   Wireguard  instead.  In the Swag folders, create the   stats.subdomain.conf  file.   ✨   Tip for those who dislike the terminal: \nyou can use   File Browser  to browse and edit your files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/stats.subdomain.conf\n  Enter insert mode with   i  and paste the following config:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   stats.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app uptime-kuma;\n           set   $upstream_port   3001  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n  Press   Esc , then save and exit with   :x  and   Enter .  In Dockge, edit the SWAG compose and add the Uptime-Kuma network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :                   # Link container to custom network \n         # ...           \n         -   uptime-kuma              # Name of the declared network\n   \n   networks  :                        # Define custom network\n     # ...\n     uptime-kuma  :                   # Name of the declared network\n       name  :   uptime-kuma_default    # Actual name of the external network\n       external  :   true               # Specifies it's an external network\n  Restart the stack and wait until SWAG is fully operational.     Here we assume that the network name of Uptime-Kuma is   uptime-kuma_default . You can verify the connection by visiting SWAG's dashboard at   http://yourserverip:81 .  That's it! Uptime-Kuma is now exposed, and you can access it via   https://stats.mydomain.com .   ✨   Tip:   \nYou can protect this app with Authentik by opening   stats.subdomain.conf  and uncommenting the lines:   include /config/nginx/authentik-server.conf;  \nand   include /config/nginx/authentik-location.conf; .  \nDon’t forget to   create an application and provider in Authentik . If you want the public stats page to be accessible without authentication:   Edit the Uptime-Kuma provider  In   Advanced Protocol Settings > Authenticated Paths , enter:     ^/$\n   ^/status\n   ^/assets/\n   ^/assets\n   ^/icon.svg\n   ^/api/.*\n   ^/upload/.*\n   ^/metrics\n  Redeploy the stack.  Uptime-Kuma will then be publicly reachable via   https://stats.mydomain.com .   ✨   Tip:  If you're using Authentik and don't mind exposing the admin panel to your local network, you can disable Uptime-Kuma's native authentication in its settings and rely solely on Authentik.  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":169,"path":170,"dir":163,"title":171,"description":7,"keywords":172,"body":174},"content:3.serveex:4.monitoring:2.dozzle.md","/serveex/monitoring/dozzle","Dozzle",[106,173],"Exposing Dozzle with Swag","   Dozzle   🎯   Goals:   Install Dozzle  Expose Dozzle with Swag   Dozzle  is a container that lets you access logs from your other containers and display them in real time through a user-friendly interface. It's a simple way to browse logs and retrieve information from the history.    Installation   Folder structure     root\n   └──   docker\n       └──   dozzle\n           └──   data\n  Open Dockge, click on   compose , name the stack   dozzle , then copy and paste the following:     ---\n   services  :\n     dozzle  :\n       container_name  :   dozzle\n       image  :   amir20/dozzle:latest\n       ports  :\n         -   9135:8080\n       env_file  :\n         -   .env\n       environment  :\n         -   DOZZLE_HOSTNAME=${DOMAIN}\n       volumes  :\n         -   /var/run/docker.sock:/var/run/docker.sock\n   ✨   Tip:  Add the watchtower label to each container to automate updates     services  :\n     dozzle  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Fill in your domain name in the   .env  file, for example:     DOMAIN  =dozzle.mydomain.com\n  Deploy the container. Go to   http://yourserverip:9135 . Voilà, your Dozzle web UI is up and running!  Exposing Dozzle with Swag      Dozzle does not use multi-factor authentication. Exposing Dozzle to the internet could compromise the connected machines. Only do this if you use a multi-factor authentication system like   Authentik . Otherwise, do not expose it with SWAG and instead use a VPN like   Wireguard .  You may want to access Dozzle remotely and on all your devices. To do so, we’ll expose Dozzle via Swag.   📋   Before you begin:   \nWe assume you have created a subdomain like   dozzle.mydomain.com  in your   DNS zone  with a   CNAME  pointing to   mydomain.com  and that,   unless you're using Cloudflare Zero Trust , you’ve redirected port   443  from your router to port   443  on your server in your   NAT rules .  Go to Dockge and edit the SWAG compose file to add Dozzle’s network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :                   # Connects the container to a custom network\n         # ...           \n         -   dozzle                   # Network name declared in the stack\n       \n   networks  :                        # Defines the custom network\n     # ...\n     dozzle  :                        # Network name declared in the stack\n       name  :   dozzle_default         # Actual name of the external network\n       external  :   true               # Indicates it's an externally defined network\n  Redeploy the stack by clicking “Deploy” and wait for SWAG to be fully operational.     We assume the Dozzle network name is   dozzle_default . You can verify the connection is working by visiting the SWAG dashboard at   http://yourserverip:81 .  In the Swag folder, create the   dozzle.subdomain.conf  file.   ✨   Tip:  You can use   File Browser  to browse and edit files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/dozzle.subdomain.conf\n  Enter edit mode by pressing   i  and paste the configuration below:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   dozzle.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app dozzle;\n           set   $upstream_port   8080  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n  Press   Esc , then save and exit by typing   :x  and pressing   Enter .  And there you go, Dozzle is now exposed!   ✨ You can protect this app with Authentik by opening   dozzle.subdomain.conf  and removing the   #  in front of    include   /config/nginx/authentik-server.conf;  and    include   /config/nginx/authentik-location.conf; . Don’t forget to   create an application and a provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":176,"path":177,"dir":163,"title":178,"description":7,"keywords":179,"body":181},"content:3.serveex:4.monitoring:3.speedtest-tracker.md","/serveex/monitoring/speedtest-tracker","Speedtest Tracker",[106,180],"Expose Speedtest Tracker","   Speedtest Tracker   🎯   Objectives:   Install Speedtest Tracker  Expose Speedtest Tracker with SWAG   Speedtest Tracker  is a container that allows you to schedule regular speed tests in order to log your server's internet connection status.    Installation      We will use the Docker image maintained by   LinuxServer.io  File structure:     root\n   └──   docker\n       └──   speedtest-tracker\n           └──   data\n               └──   config\n  In a terminal, generate a key using the following command:     echo   -n   'base64:'  ;   openssl   rand   -base64   32  ;\n  Take note of the key.  Open Dockge, click on   compose , name the stack   speedtest-tracker , then paste the following:     ---\n   services  :\n       speedtest-tracker  :\n           image  :   lscr.io/linuxserver/speedtest-tracker:latest\n           restart  :   unless-stopped\n           container_name  :   speedtest-tracker\n           ports  :\n               -   ${PORT}:80\n           environment  :\n               -   PUID=${PUID}\n               -   PGID=${GUID}\n               -   TZ=Europe/Paris\n               -   APP_KEY=${API_KEY}\n               -   DB_CONNECTION=sqlite\n               -   SPEEDTEST_SCHEDULE=${SCHEDULE}\n           volumes  :\n               -   /docker/speedtest-tracker/data/config:/config\n  Find your   PUID  and   GUID  by running the following command:     id   yourusername\n  In the   .env  file, set the variable   API_KEY  with the key you generated and add a cron-style test schedule, as well as your   PUID  and   GUID , for example:     SCHEDULE  =15 */6 * * *   # every 6 hours\n   API_KEY  =base64:zihejehkj8_nzhY/  OjeieR  =   # your key\n   PUID  =1000\n   GUID  =1000\n   PORT  =3225   # port to access the web UI\n   ✨   Tip:  You can configure additional environment variables by referring to the   official documentation .  Deploy the container and go to   http://yourserverip:3225 . Log in with the account   admin@exemple.com  and the password   password . Don’t forget to change your ID and password once logged in!  Expose Speedtest Tracker    📋   Prerequisites:  \nWe assume that you've already created a subdomain like   speedtest.yourdomain.com  in your   DNS zone  with a   CNAME  pointing to   yourdomain.com , and   unless you’re using Cloudflare Zero Trust , you've also forwarded port   443  from your router to port   443  of your server in your   NAT rules .  Now we want to expose Speedtest Tracker to the internet so you can access it remotely. We assume you've set up the DNS   CNAME  for   speedtest.yourdomain.com  pointing to   yourdomain.com .     Speedtest Tracker does not use multi-factor authentication. Exposing it on the internet could compromise connected devices. Do so only if you use a multi-factor system like   Authentik . Otherwise, avoid using SWAG and prefer a VPN like   Wireguard .  Open the   speedtest.subdomain.conf  file:     sudo   vi   /docker/swag/config/nginx/proxy-confs/speedtest.subdomain.conf\n  Configure it like this:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n       \n       server_name   speedtest.*;  \n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; } \n   \n       # Authentication options (uncomment as needed)\n       #include /config/nginx/ldap-server.conf;\n       #include /config/nginx/authelia-server.conf;\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # Basic auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # Per-location authentication\n           #include /config/nginx/ldap-location.conf;\n           #include /config/nginx/authelia-location.conf;\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           \n           set   $upstream_app speedtest-tracker;\n           set   $upstream_port   3225  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Save and exit. The configuration will update in a few seconds.     By default, SWAG doesn’t know the name \"speedtest-tracker\". To allow access, you need to add Speedtest Tracker’s network to SWAG’s   compose.yml .  Go to Dockge, and edit SWAG’s compose to include Speedtest Tracker’s network:     services  :\n     swag  :\n       container_name  :   # ...\n       # ...\n       networks  :\n         # ...\n         -   speedtest-tracker\n   \n   networks  :\n     # ...\n     speedtest-tracker  :\n       name  :   speedtest-tracker_default\n       external  :   true\n  Restart the stack by clicking \"Deploy\" and wait for SWAG to be fully up.     This assumes the Speedtest Tracker network is named   speedtest-tracker_default . You can verify the connection by visiting SWAG’s dashboard at   http://yourserverip:81 .  Wait a moment, then visit   https://speedtest.yourdomain.com  in your browser — you should be redirected to Speedtest Tracker. You can check service status via the dashboard (  http://yourserverip:81  from the local network).   ✨ You can protect this app with Authentik by opening   speedtest.subdomain.conf  and uncommenting   include /config/nginx/authentik-server.conf;  and   include /config/nginx/authentik-location.conf; .  \nDon’t forget to   create an application and provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":183,"path":184,"dir":163,"title":185,"description":7,"keywords":186,"body":188},"content:3.serveex:4.monitoring:4.beszel.md","/serveex/monitoring/beszel","Beszel",[106,187],"Expose Beszel with Swag","   Beszel   🎯   Objectives:   Install Beszel  Monitor the local server  Monitor a remote server  Expose Beszel with Swag   Beszel  is a container that gives you real-time access to hardware information from your servers and allows historical tracking. CPU activity, disk usage, temperatures, RAM—nothing escapes your monitoring. Beszel also lets you configure notifications and alerts when your predefined thresholds are exceeded.  Beszel includes a hub with a web UI and an agent that collects data from your server or a remote server.    Installation   Folder structure     root\n   └──   docker\n       └──   beszel\n           ├──   data\n           └──   socket\n  Open Dockge, click   compose , name the stack   beszel , and paste the following:     ---\n   services  :\n     beszel  :\n       image  :   henrygd/beszel:latest\n       container_name  :   beszel\n       restart  :   unless-stopped\n       ports  :\n         -   ${PORT}:8090\n       volumes  :\n         -   ./data:/beszel_data\n         -   ./socket:/beszel_socket\n   \n     beszel-agent  :\n       image  :   henrygd/beszel-agent:latest\n       container_name  :   beszel-agent\n       restart  :   unless-stopped\n       network_mode  :   host\n       volumes  :\n         -   ./socket:/beszel_socket\n         -   /var/run/docker.sock:/var/run/docker.sock:ro\n       environment  :\n         LISTEN  :   /beszel_socket/beszel.sock\n         # Do not remove quotes around the key\n         KEY  :   ${KEY}\n   ✨   Tip:  Add the Watchtower label to each container to automate updates.     services  :\n     beszel  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Fill out the   .env  file, for example:     PORT  =8090   # web UI port\n   KEY  =   # private key to retrieve from Beszel when adding a system\n  For the   KEY  value, you'll need to launch Beszel once to get it.  Deploy the container and go to   http://yourserverip:8090 . Your Beszel web UI is now accessible!      If it fails:  check your firewall rules.  Add local server information  Now that the web UI is accessible, you need to push local server information into it. Just add a machine via the web UI and configure it like this:    Note the private key and confirm. Enter the key in your   .env  file in Dockge and redeploy the stack. Once done, your server will appear in the web UI:    Add a remote server  You can also monitor a remote server. To do so, run the agent on the remote server. Add a new machine in Beszel and fill in:   The name displayed for your remote server  The IP address or domain name of the remote server  The listening port (e.g.,   45876 )  Beszel will suggest a   compose.yaml  to deploy on the remote server, or you can use:     ---\n   services  :\n     beszel-agent  :\n       image  :   henrygd/beszel-agent\n       container_name  :   beszel-agent\n       restart  :   unless-stopped\n       network_mode  :   host\n       volumes  :\n         -   /var/run/docker.sock:/var/run/docker.sock:ro\n       environment  :\n         LISTEN  :   ${PORT}\n         KEY  :   ${KEY}\n  And in   .env :     PORT  =45876   # communication port between hub and remote agent\n   KEY  =   # private key from Beszel when adding the system\n  Deploy the stack on the remote server. Data will begin flowing into the web UI after a few seconds.      If it fails:  check your firewall rules.  Expose Beszel with Swag      Beszel does not support multi-factor authentication. Exposing it on the internet could compromise connected machines. Only do this if you're using a system like   Authentik . Otherwise, do not expose with SWAG—use a VPN like   Wireguard  instead.  If you want to access Beszel remotely from all your devices, expose it using Swag.   📋   Prerequisite:   \nYou must have created a DNS subdomain like   beszel.mydomain.com  with a   CNAME  pointing to   mydomain.com , and—unless you're using Cloudflare Zero Trust—you must have forwarded port   443  on your router to your server’s   443  port via   NAT rules .  In Dockge, edit Swag's compose file and add Beszel’s network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ...\n        networks  :\n         # ...\n         -   beszel              # network declared in the stack\n   \n   networks  :\n     # ...\n     beszel  :\n       name  :   beszel_default    # actual external network name\n       external  :   true\n  Redeploy the stack and wait for Swag to become fully operational.     We assume the network name is   beszel_default . You can check connectivity by visiting Swag's dashboard at   http://yourserverip:81 .  In Swag’s config folders, create   beszel.subdomain.conf .   ✨   Tip:  Use   File Browser  to browse and edit files instead of terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/beszel.subdomain.conf\n  Press   i  to enter insert mode and paste:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   beszel.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           #include /config/nginx/ldap-location.conf;\n           #include /config/nginx/authelia-location.conf;\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app beszel;\n           set   $upstream_port   8090  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Press   Esc , type   :x , and hit   Enter  to save and exit.  That’s it—Beszel is now exposed!   ✨ You can protect this app with Authentik by opening   beszel.subdomain.conf  and removing the   #  in front of   include /config/nginx/authentik-server.conf;  and   include /config/nginx/authentik-location.conf; . Don’t forget to   create an application and provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":190,"path":191,"dir":163,"title":192,"description":7,"keywords":193,"body":195},"content:3.serveex:4.monitoring:5.upsnap.md","/serveex/monitoring/upsnap","UpSnap",[106,194],"Exposing UpSnap with Swag","   UpSnap   🎯   Goals:   Install UpSnap  Expose UpSnap with Swag   UpSnap  is a container that allows you to remotely power on, shut down, or put your machines to sleep. It mainly uses Wake-On-Lan (WoL) over the network and offers advanced features.    Installation   Folder structure     root\n   └──   docker\n       └──   upsnap\n           └──   data\n  Open Dockge, click on   compose , name the stack   upsnap , then copy and paste the following:     ---\n   services  :\n     upsnap  :\n       container_name  :   upsnap\n       image  :   ghcr.io/seriousm4x/upsnap:5\n       network_mode  :   host\n       restart  :   unless-stopped\n       volumes  :\n         -   /docker/upsnap/data:/app/pb_data\n       environment  :\n         -   TZ=Europe/Paris\n         -   UPSNAP_SCAN_RANGE=${SCAN_RANGE}\n         -   UPSNAP_SCAN_TIMEOUT=500ms\n         -   UPSNAP_PING_PRIVILEGED=true\n       dns  :\n         -   ${DNS}\n       entrypoint  :   /bin/sh -c \"./upsnap serve --http 0.0.0.0:8095\"\n       healthcheck  :\n         test  :   curl -fs \"http://localhost:8095/api/health\" || exit 1\n         interval  :   10s\n   ✨   Tip:  Add the watchtower label to each container to automate updates     services  :\n     upsnap  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Fill in the   .env , for example:     RANGE  =192.168.1.0/24   # scans all devices on the local network with an IP between 192.168.0.1 and 192.168.1.255\n   DNS  =192.168.1.1   # DNS IP to resolve domain names, typically your router’s IP\n  Deploy the container and go to   http://yourserverip:8095 . Just follow the steps to create your account!      If it fails:  check your firewall rules.  Exposing UpSnap with Swag      UpSnap does not support multi-factor authentication. Exposing it on the internet could compromise connected machines. Do this only if you're using a multi-factor authentication system like   Authentik . Otherwise, avoid exposing it with SWAG and use a VPN like   Wireguard  instead.  You may want to access it remotely from all your devices. To do so, we'll expose UpSnap via Swag.   📋   Beforehand:   \nWe assume you've created a subdomain in your   DNS zone , such as   upsnap.yourdomain.com  with a   CNAME  to   yourdomain.com . Also, unless you're using Cloudflare Zero Trust, you should have already forwarded port   443  from your router to port   443  on your server in your   NAT rules .  Go to Dockge, and edit the SWAG compose by adding the UpSnap network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :                         # Connects the container to the custom network \n         # ...           \n         -   upsnap                         # Network name declared in the stack\n       \n   networks  :                              # Defines the custom network\n     # ...\n     upsnap  :                   # Network name declared in the stack\n       name  :   upsnap_default               # Actual name of the external network\n       external  :   true                     # Indicates it's an external network\n  Restart the stack by clicking \"deploy\" and wait for SWAG to be fully operational.     Here we assume the network name for upsnap is   upsnap_default . You can check the connection in the SWAG dashboard at   http://yourserverip:81 .  In the Swag folders, create the file   upsnap.subdomain.conf .   ✨   Tip:  You can use   File Browser  to navigate your files and edit documents instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/upsnap.subdomain.conf\n  Enter edit mode by pressing   i , and paste the following configuration:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   upsnap.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app upsnap;\n           set   $upstream_port   8095  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n  Press   Escape , then save and exit by typing   :x  and pressing   Enter .  And that’s it — you’ve exposed UpSnap!   ✨ You can protect this app with Authentik by opening   upsnap.subdomain.conf  and removing the   #  in front of    include   /config/nginx/authentik-server.conf;  and    include   /config/nginx/authentik-location.conf; . Don’t forget to   create an application and provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":197,"path":198,"dir":199,"title":200,"description":7,"keywords":201,"body":205},"content:3.serveex:5.media:1.plex.md","/serveex/media/plex","media","Plex",[202,203,204],"Install Plex","Configure Plex","Expose Tautulli with Swag","   Plex   🎯   Objectives:   Install Plex  Install Tautulli  Access media from outside your network   Plex  is a self-hosted video streaming platform for managing your movie or TV show library and playing them locally or remotely. Plex has apps for TV, Android, iOS, Windows, and macOS, allowing you to stream your library just like Netflix.  With   Plex Pass , you can also organize and play your music content similar to Spotify, the difference being that it’s your content, hosted and streamed from your server.    We'll also install   Tautulli , a tool that provides detailed stats about Plex. As always, we'll use linuxserver.io images where possible.    More info on the Plex container   More info on the Tautulli container     You’ll need to create a   Plex.tv  account. You don’t need to expose your Plex service; it will be accessible through the platform. Your Plex server will be manageable directly from your account.  Install Plex   Folder structure:     root\n   ├──   docker\n   │     ├──   plex        \n   │     │     ├──   compose.yml\n   │     │     ├──   .env\n   │     │     ├──   config\n   │     │     └──   transcode\n   │     └──   tautulli\n   │         └──   config\n   └──   media\n       ├──   tvseries\n       ├──   movies\n       └──   library\n  Create the   movies ,   tvseries , and   library  folders in   /media :     mkdir   -p   /media/movies   /media/library   /media/tvseries\n  Open Dockge in your browser and click   compose .  \nName the stack   plex  and add the following config:     ---\n   services  :\n     linuxserver_plex  :\n       image  :   ghcr.io/linuxserver/plex:amd64-latest\n       container_name  :   plex\n       network_mode  :   host\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${GUID}\n         -   TZ=Europe/Paris\n         -   VERSION=docker\n       volumes  :\n         -   /docker/plex/config:/config\n         -   /docker/plex/transcode:/transcode\n         -   /media:/media\n       restart  :   unless-stopped\n       mem_limit  :   4096m\n       mem_reservation  :   2048m\n       devices  :\n         -   /dev/dri:/dev/dri\n   \n     tautulli  :\n       image  :   lscr.io/linuxserver/tautulli:latest\n       container_name  :   tautulli\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${GUID}\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/tautulli/config:/config\n       ports  :\n         -   8181:8181\n       restart  :   unless-stopped\n   ✨ Add the Watchtower label to each container to automate updates:     services  :\n     plex  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n   \n     tautulli  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Find your PUID and GUID by running:     id   username\n  Fill in your   .env  file with the retrieved values, for example:     PUID  =1000\n   GUID  =1000\n  Deploy the stack.  The local interface is available at   http://yourserverip:32400/web/index.html .  \nTautulli is accessible at   http://yourserverip:8181 .     You must be on your local network during Plex's initial setup. Otherwise, the URL will redirect to your Plex account without detecting your server. A VPN won't help. If you have no choice,   you can handle the setup remotely via SSH tunnel .  Configure Plex   Plex offers a range of free movies/shows. After creating your account, I recommend disabling everything in the   Online Services  section to keep your library clean.  Then go to the   Remote Access  section and manually select a port (we’ll use   1234 ). It's best not to use the default port.     On your router, forward TCP port   1234  to port   32400  for your server’s IP using   NAT rules .  Once done, return to Plex to verify that remote access is functional.      If it fails:  check your firewall rules and allow port   32400  on your server.   If you have PlexPass and a GPU or iGPU, enable   hardware acceleration  in the   Transcoder  section.  In   Settings > Library , enable   Update my library automatically .  In   Manage > Library , add or edit libraries and point to   /media/movies  for movies and   /media/tvseries  for series.  And that’s it! You now have a working Plex server!  Simply add your media to   /media/movies  and   /media/tvseries  on your server. You can then install the Plex app on your devices and watch your favorite content locally or remotely.     If your media is stored on a network disk (e.g. NAS or external hard drive over the network), refer to the   Samba mount guide  so Plex can access it.  Expose Tautulli with Swag   You don’t need to expose Plex, as it's accessible via your Plex account on plex.tv.  However, you may want to expose Tautulli so you can view stats from a simple URL when you're not home.     We assume you have the subdomain   tautulli.mydomain.com  with a   CNAME  pointing to   mydomain.com  in your   DNS zone . And of course,   unless you use Cloudflare Zero Trust , your box's port   443  must be forwarded to your server's port   443  in   NAT rules .  Go to Dockge and edit SWAG’s compose file by adding Tautulli’s network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :                # Attach container to custom network \n         # ...           \n         -   tautulli              # Name of the declared network\n   \n   networks  :                     # Define the custom network\n     # ...\n     tautulli  :                   # Declared network name\n       name  :   tautulli_default    # Actual external network name\n       external  :   true            # Marks it as externally defined\n  Redeploy the stack and wait for SWAG to be fully operational.     Here we assume the Tautulli network name is   tautulli_default . You can check the connection by visiting SWAG’s dashboard at   http://yourserverip:81 .  Copy and rename the file   tautulli.subdomain.conf.sample  to   tautulli.subdomain.conf , then edit it:   ✨   Tip:  Use   File Browser  to navigate and edit files instead of using terminal commands.     sudo   cp   /docker/swag/config/nginx/proxy-confs/tautulli.subdomain.conf.sample   /docker/swag/config/nginx/proxy-confs/tautulli.subdomain.conf\n   sudo   vi   /docker/swag/config/nginx/proxy-confs/tautulli.subdomain.conf\n  Ensure the configuration matches the following. If needed, press   i  to edit:     ## Version 2023/05/31\n   # make sure that your tautulli container is named tautulli\n   # make sure that your dns has a cname set for tautulli\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   tautulli.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app tautulli;\n           set   $upstream_port   8181  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   \n       location   ~   (/tautulli)?/api   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app tautulli;\n           set   $upstream_port   8181  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   \n       location   ~   (/tautulli)?/newsletter   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app tautulli;\n           set   $upstream_port   8181  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   \n       location   ~   (/tautulli)?/image   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app tautulli;\n           set   $upstream_port   8181  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n   ✨ You can protect this app with Authentik by removing the   #  before   include /config/nginx/authentik-server.conf;  and   include /config/nginx/authentik-location.conf; . Don’t forget to   create an application and provider in Authentik .  Press   Esc  then save and quit by typing   :x  Wait a few minutes, then open   http://tautulli.mydomain.com  in your browser.      If it fails:  check your firewall rules.  And you're done!  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":207,"path":208,"dir":199,"title":209,"description":7,"keywords":210,"body":214},"content:3.serveex:5.media:2.qbittorrent.md","/serveex/media/qbittorrent","Qbittorrent",[211,212,213],"Configuration","Deployment","Exposing the Web UI","   Qbittorrent   🎯   Goals:   Install and configure Qbittorrent  Securely connect to the BitTorrent network using Gluetun and Proton VPN    To safely download your favorite media, we'll build a system using:    Qbittorrent  as the BitTorrent client   Proton VPN Plus , a VPN to secure your traffic. You need a subscription (promos available) to access the BitTorrent protocol. You can also use another VPN as long as it supports BitTorrent.   Gluetun   Qbittorrent port update  to automatically update the VPN port (which changes regularly).  The   VueTorrent  mod for a modern and intuitive UI.  Here’s the system we’ll set up:    Configuration   Folder structure     root\n   ├──   docker\n   │     └──   seedbox\n   │         ├──   qbittorrent\n   │         │     └──   config\n   │         ├──   gluetun\n   │         ├──   compose.yaml\n   │         └──   .env\n   │         \n   └──   media   #linked to Plex and Qbittorrent\n       ├──   downloads   #generic downloads, selected in settings\n       ├──   movies   #used for downloading movies\n       └──   tvseries   #used for downloading TV shows\n  If not already done, create the   downloads  folder under   /media :     mkdir   -P   /media/downloads\n  Open Dockge, click on   compose , and name the stack   seedbox . Paste the following config:     \n   services  :\n     qbit  :\n       image  :   ghcr.io/linuxserver/qbittorrent:libtorrentv1\n       container_name  :   qbittorrent\n       restart  :   unless-stopped\n       network_mode  :   service:gluetun\n       mem_limit  :   4g\n       environment  :\n         -   DOCKER_MODS=ghcr.io/gabe565/linuxserver-mod-vuetorrent|ghcr.io/t-anc/gsp-qbittorent-gluetun-sync-port-mod:main\n         -   TZ=Europe/Paris\n         -   PUID=${PUID}\n         -   PGID=${GUID}\n         -   WEBUI_PORT=${UI_PORT}\n         -   GSP_GTN_API_KEY=${GSP_KEY}\n         -   GSP_QBT_USERNAME=${ID}\n         -   GSP_QBT_PASSWORD=${PW}\n       volumes  :\n         -   /docker/seedbox/qbittorrent/config:/config\n         -   /media:/media\n       depends_on  :\n         -   gluetun\n   \n     gluetun  :\n       image  :   qmcgaw/gluetun:v3.40.1\n       container_name  :   gluetun\n       restart  :   unless-stopped\n       mem_limit  :   4g\n       volumes  :\n         -   /docker/gluetun/config.toml:/gluetun/auth/config.toml:ro\n       devices  :\n         -   /dev/net/tun:/dev/net/tun\n       ports  :\n         -   ${UI_PORT}:5695   # Port de la web-ui\n         -   8000:8000   # Port de controle de Gluetun\n       cap_add  :\n         -   NET_ADMIN\n       environment  :\n         -   TZ=Europe/Paris\n         -   VPN_SERVICE_PROVIDER=protonvpn\n         -   VPN_PORT_FORWARDING=on\n         -   VPN_PORT_FORWARDING_PROVIDER=protonvpn\n         -   VPN_TYPE=wireguard\n         -   WIREGUARD_PRIVATE_KEY=${PR_KEY}\n         -   SERVER_COUNTRIES=France\n         -   PORT_FORWARD_ONLY=on\n   ✨   Tip:  Add the Watchtower label in each container to automate updates     services  :\n     qbittorrent  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n     gluetun  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Before editing the   .env  in Dockge, let's configure the download port update. Proton and most VPNs rotate the forwarding port, which must be communicated to Qbittorrent.  We’ve added the mod   ghcr.io/t-anc/gsp-qbittorent-gluetun-sync-port-mod  to the container.  We now need to allow the mod to fetch info from Gluetun, which only allows encrypted communication via its API.  Open a terminal to generate the authentication key:     sudo   docker   run   --rm   qmcgaw/gluetun   genkey\n  Note the key, then create the   /docker/gluetun  folder:     sudo   mkdir   /docker/gluetun\n  Create the   config.toml  file:     sudo   vi   /docker/gluetun/config.toml\n  Press   i  to edit and enter:     [[roles]]\n   name = \"t-anc/GSP-Qbittorent-Gluetun-sync-port-mod\"\n   routes = [\"GET /v1/openvpn/portforwarded\"]\n   auth = \"apikey\"\n   apikey = \"your_key_here\" # key you just generated\n  Press   Esc  then type   :x  to save and exit.  In Dockge, fill in the variables in   .env :     PUID  =\n   GUID  =\n   UI_PORT  =\n   PR_KEY  =\n   GSP_KEY  =   # the key you generated and entered in config.toml\n   ID  =\n   PW  =\n  Detailed info:     Variable  Description  Example     PUID  User ID (  id yourusername )   1000    GUID  Group ID (  id yourusername )   1000    UI_PORT  Port for accessing the web UI   5695    PR_KEY  Private key from Proton   buKsjNHLyzKMM1qYnzOy4s7SHfly    GSP_KEY  Key you generated for port update   MnBa47MeVmk7xiv    ID  Qbittorrent UI login username   user    PW  Qbittorrent UI password   password  Deployment   Once done, deploy the container.      Startup logs will show a temporary password for   admin  user  Login at   http://server-ip:5695  (or the port you set).      If login fails:  check your firewall rules.  Change your username and password in the \"webui\" settings.  You're done! In Qbittorrent settings, under \"Downloads\", set   /media/downloads  as the default folder.  When adding a download, remember to select the proper directory so Plex can sync correctly (  /media/movies  or   /media/tvseries ). You can also automate this with categories and folders.  Exposing the Web UI      Qbittorrent does not support multi-factor authentication. Exposing it to the internet may put your system at risk. Only do this if you use MFA via   Authentik . Otherwise, don’t expose it with SWAG—use a VPN like   Wireguard  instead.  To start downloads from outside your home, without a VPN, you can expose the Qbittorrent web UI.     We assume you have the subdomain   seedbox.mydomain.com  with a   CNAME  pointing to   mydomain.com  in   DNS zone . And that port   443  on your router is forwarded to your server in   NAT rules , unless you’re using Cloudflare Zero Trust.  In Dockge, edit the SWAG compose file and add Gluetun’s network:     services  :\n     swag  :\n       container_name  :   # ...\n       # ...\n       networks  :\n         # ...\n         -   seedbox\n   \n   networks  :\n     # ...\n     seedbox  :\n       name  :   seedbox_default\n       external  :   true\n  Click \"Deploy\" and wait for SWAG to fully initialize.     We assume the network name is   seedbox_default . You can confirm by checking the SWAG dashboard at   http://server-ip:81 .  Now create/edit   seedbox.subdomain.conf .   ✨   Terminal-free tip:  use   File Browser  to edit files instead of using the terminal.     sudo   vi   /docker/swag/config/nginx/proxy-confs/seedbox.subdomain.conf\n  Press   i  and paste the following config (check the port):     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   seedbox.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app gluetun;\n           set   $upstream_port   5555  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n   ✨ You can secure this app with Authentik by uncommenting the   authentik-server.conf  and   authentik-location.conf  lines. Don’t forget to   create an app and provider in Authentik .  Press   Esc , type   :x  to save and quit.  Wait a few minutes, then go to   https://seedbox.mydomain.com —you should land on the Qbittorrent interface.  And that’s it! You now have a ready-to-use media center.    .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":216,"path":217,"dir":199,"title":218,"description":7,"keywords":219,"body":222},"content:3.serveex:5.media:3.servarr.md","/serveex/media/servarr","Automation",[220,221],"Install the Apps","Exposing Overseerr with SWAG","   Servarr   🎯   Goals:   Automate movie and TV show downloads using Radarr, Sonarr, Bazarr, Prowlarr, and Overseerr.   Servarr  is a suite of applications developed to automate the downloading, updating, and management of media. Here, we'll focus on movies and TV shows with the goal of:   Selecting a movie from a catalog through a web interface.  Sitting back and enjoying it on Plex a few minutes later.  Simple.    We’ll start by deploying the stack and then proceed to configure each app and understand how they work.  Install the Apps   Docker Compose  Folder structure:     root\n   ├──   docker\n   │     ├──   plex        \n   │     │     ├──   compose.yml\n   │     │     ├──   config\n   │     │     └──   transcode\n   │     ├──   tautulli\n   │     │     └──   config\n   │     ├──   sonarr\n   │     │     └──   config\n   │     ├──   radarr\n   │     │     └──   config\n   │     ├──   bazarr\n   │     │     └──   config\n   │     ├──   prowlarr\n   │     │     └──   config\n   │     └──   overseerr\n   │         └──   config\n   └──   media\n       ├──   downloads\n       ├──   tvseries\n       ├──   movies\n       └──   library\n      Warning:  Make sure to follow this file structure carefully, especially the   media  folder. This folder must be mounted   exactly the same way  in both the   Qbittorrent  compose file (  /your/path/media:/media ) and the   arr  applications.  \nIf not, the   arr  apps may not recognize the path provided by Qbittorrent and will fail to create   hardlinks .  \nWithout hardlinks, the   arr  apps will copy the files instead—  doubling the space used  on your storage.  Open Docker and your   plex  stack. Modify the compose file as follows:     ---\n   services  :\n     linuxserver_plex  :\n       image  :   ghcr.io/linuxserver/plex:latest\n       container_name  :   plex\n       network_mode  :   host\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${PGID}\n         -   TZ=Europe/Paris\n         -   VERSION=docker\n         -   PLEX_CLAIM=   #optional\n       volumes  :\n         -   /docker/plex/config:/config\n         -   /docker/plex/transcode:/transcode   #optional\n         -   ${MEDIA_PATH}:/media\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n       restart  :   unless-stopped\n       mem_limit  :   4096m\n       mem_reservation  :   2048m\n       devices  :\n         -   /dev/dri:/dev/dri\n   \n     tautulli  :\n       image  :   lscr.io/linuxserver/tautulli:latest\n       container_name  :   tautulli\n       environment  :\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/tautulli/config:/config\n       ports  :\n         -   8181:8181\n       restart  :   unless-stopped\n     \n     sonarr  :\n       image  :   lscr.io/linuxserver/sonarr:latest\n       container_name  :   sonarr\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${PGID}\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/sonarr/config:/config\n         -   ${MEDIA_PATH}:/media\n       ports  :\n         -   8989:8989\n       restart  :   unless-stopped\n   \n     radarr  :\n       image  :   lscr.io/linuxserver/radarr:latest\n       container_name  :   radarr\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${PGID}\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/radarr/config:/config\n         -   ${MEDIA_PATH}:/media\n       ports  :\n         -   7878:7878\n       restart  :   unless-stopped\n   \n     prowlarr  :\n       image  :   lscr.io/linuxserver/prowlarr:latest\n       container_name  :   prowlarr\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${PGID}\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/prowlarr/data:/config\n       ports  :\n         -   9696:9696\n       restart  :   unless-stopped\n   \n     overseerr  :\n       image  :   lscr.io/linuxserver/overseerr:latest\n       container_name  :   overseerr\n       dns  :\n         -   1.1.1.1\n         -   8.8.8.8\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${PGID}\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/overseerr/config:/config\n       ports  :\n         -   5055:5055\n       restart  :   unless-stopped\n   \n     bazarr  :\n       image  :   lscr.io/linuxserver/bazarr:latest\n       container_name  :   bazarr\n       restart  :   unless-stopped\n       environment  :\n         -   PUID=1000   \n         -   PGID=1000\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/bazarr/config:/config\n         -   ${MEDIA_PATH}:/media\n       ports  :\n         -   6767:6767\n   ✨ Add the Watchtower label to each container to automate updates     services  :\n     plex  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n   \n     tautulli  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Set your   .env  file with the variables below:     PUID  =\n   GUID  =\n   MEDIA_PATH  =\n     Variable  Description  Example     PUID  Set using your user info (check with   id yourusername )   1000    GUID  Same as above   1000    MEDIA_PATH  Path to your media folder, here:   /media . It must match the one used by Qbittorrent.   /media  Deploy the stack.  Configure Radarr   Radarr queries your torrent sources and lets you define the type of releases to prioritize. It can also upgrade your movies if a better version is available.  Once deployed, visit   http://yourserverip:7878 .      If it fails:  check your firewall rules.  Create an account and choose   forms login .  Add a   root folder   Go to   Settings > Media Management .  Add a root folder and select   /media/movies .      Warning:  If you already have movies in   movies  from Qbittorrent, do not let Radarr add them. Radarr might modify them, which could stop seeding in Qbittorrent.  Configure Profiles  Go to   Settings > Profiles . These are your default quality profiles. When you make a request, you're selecting one of these. For example, configure the “any” profile by unchecking everything except what is shown in the image and ordering them accordingly. This makes Radarr search for 4K REMUX first, then go down the list if unavailable.    Add Qbittorrent  In   Settings > Download Clients , add Qbittorrent.   Use your server IP as   Host  and port   5695  if following this guide.  Provide your Qbittorrent   Username  and   Password .  Click   Test .  If successful, click   Save .  Connect to Plex  Go to   Settings > Connect , add a new connection and choose   Plex Media Server .   Use   plex  or your server IP for   Host .  Port:   32400  Click the blue \"authenticate with Plex.tv\" button and log into Plex.  Press   Test , then   Save  if successful.  Get API Key for Prowlarr and Overseerr   Go to   Settings > General  and copy your   API Key  for later use.  Configure Sonarr   Sonarr queries torrent sources and defines what kind of TV series releases to prioritize. It also upgrades series when better versions are available.   Visit   http://yourserverip:8989 .  Follow the same steps as for Radarr, but use   /media/tvseries  as the root folder.      If it fails:  check your firewall rules.  Configure Prowlarr   Prowlarr acts as a proxy to manage your torrent indexers and link them to Radarr and Sonarr.  Go to   http://yourserverip:9696  and create an account, using   forms login .      If it fails:  check your firewall rules.  Add an Indexer   Go to the   Indexers  section and add your torrent indexer.  Add Radarr and Sonarr  In   Settings > Apps , add Radarr and Sonarr with the following details:   Prowlarr Server:   http://prowlarr:9696  (or use server IP)  Sonarr / Radarr Server:   http://sonarr:8989  or   http://radarr:7878  API Key: use the one copied from Radarr and Sonarr.  Click   Test , then   Save  if all goes well.  Configuring Bazarr   Bazarr is an app that automatically searches for the correct subtitles in your preferred languages for all the movies and TV shows added by Radarr and Sonarr.  Go to   http://yourserverip:6767 .      If it fails:  check your firewall rules.  Go to   Settings > General  and create a username and password using   forms login .  Add a Language Profile   In   Settings > Languages , click the pink   Add new profile  button and name it.  Click the pink   Add Languages  button and add your preferred languages, e.g.,   French  and   English .  Save and exit.  At the bottom of the screen under   Default Language For Newly Added Show , check both boxes and select the profile you just created.\n   Save using the button at the top of the screen.  Add Subtitle Providers   In   Settings > Providers , add your preferred providers, for example:     Save using the button at the top of the screen.  Add Radarr and Sonarr   Go to   Settings > Sonarr  In   Address , enter   sonarr  or your server's IP address.  In   Port , enter   8989 .  In   API Key , enter Sonarr’s API key.  Click   Test .  Save using the button at the top of the screen.  Repeat the same steps for Radarr.  Configuring Overseerr    Overseerr  is an app that lets you browse a movie catalog and send requests to Sonarr and Radarr. Just browse movies or series, click   Request , and the media will automatically be downloaded according to your Radarr or Sonarr settings. If the title hasn’t been released yet, it will be downloaded automatically when available. This way, episodes of a series appear in Plex without any manual intervention.    Go to   http://yourserverip:5055  and log in with your Plex account.      If it fails:  check your firewall rules.  Add Radarr and Sonarr  When prompted, add a Radarr server:   Check   Default server .   Server name:  Radarr   Hostname or IP address:    radarr  or your server's IP   Port:    7878   API Key:  Radarr’s API key  Click   Test  at the bottom.  If the test succeeds, continue filling in the fields:    Quality Profile:  the one you configured (e.g.,   any )   Root Folder:  the Plex folder. In our examples:   /media/movies   Minimum Availability:    Announced . This allows requesting unreleased content and downloads it upon release.  Check all 3 boxes at the bottom.  Save and continue.  Now do the same for Sonarr:   Check   Default server .   Server name:  Sonarr   Hostname or IP address:    sonarr  or your server's IP   Port:    8989   API Key:  Sonarr’s API key  Click   Test  at the bottom.  If the test succeeds, continue filling in the fields:    Quality Profile:  the one you configured (e.g.,   any )   Root Folder:  the Plex folder. In our examples:   /media/tvseries   Language Profile:    Deprecated  Check all 4 boxes at the bottom.  Save and continue.  And that’s it! Just request a movie or series, then check in qBittorrent or Radarr/Sonarr. Within a few minutes, your media will be available on Plex!  Exposing Overseerr with SWAG   It can be useful to expose Overseerr if you want to send requests from outside your network without a VPN, or if you've shared your Plex library with others and want them to have Overseerr access.     We assume you have the subdomain   films.mydomain.com  with a   CNAME  pointing to   films.fr  in your   DNS zone . And that   unless you’re using Cloudflare Zero Trust , port   443  on your router is forwarded to port   443  on your server via   NAT rules .  Go to Dockge, edit the SWAG compose file, and add the Overseerr network, which is the same as Plex (since it’s in the Plex stack):     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :            # Connects the container to a custom network \n         # ...           \n         -   plex              # Name of the network declared in the stack\n       \n   networks  :                 # Defines the custom network\n     # ...\n     plex  :                   # Name of the declared network\n       name  :   plex_default    # Actual name of the external network\n       external  :   true        # Indicates it’s an external network\n  Restart the stack by clicking “Deploy” and wait until SWAG is fully operational.     Here we assume the Tautulli network is named   plex_default . You can verify the connection works by visiting the SWAG dashboard at   http://yourserverip:81 .  Create and edit the file   films.subdomain.conf :   ✨   Tip:  you can use   File Browser  to browse and edit files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/films.subdomain.conf\n  Enter insert mode by pressing   i :     ## Version 2024/07/16\n   # make sure that your overseerr container is named overseerr\n   # make sure that your dns has a cname set for overseerr\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   films.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app overseerr;\n           set   $upstream_port   5055  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   \n       location   ~   (/overseerr)?/api   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app overseerr;\n           set   $upstream_port   5055  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n  Press   Escape , then type   :x  and press   Enter  to save and exit.  Wait a few minutes, then visit   http://films.mydomain.com  in your browser.      If it fails:  check your firewall rules.  And there you go, Overseerr is now publicly accessible!  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":224,"path":225,"dir":226,"title":227,"description":7,"keywords":228,"body":230},"content:3.serveex:6.cloud:1.immich.md","/serveex/cloud/immich","cloud","Immich",[106,229],"Exposing Immich with SWAG","   Immich   🎯   Goals:  Install   Immich  to manage your photos across all your devices.   Immich  is a self-hosted photo and video management solution that replaces cloud services like Google Photos or iCloud. It offers powerful features like face recognition and geolocation.    Installation   Folder structure     root\n   └──   docker\n       └──   immich\n           ├──   library\n           ├──   compose.yaml\n           └──   .env\n  Open Dockge, click on   compose , name the stack   immich , then copy and paste the latest   docker-compose.yml    published here .      Warning : Do not add the Watchtower label to the Immich stack. Immich evolves rapidly, and automatic updates may break your installation.  Configure the   .env  file by copying the latest version   from here  and follow the comments in the file.     If you're using a NAS or a network-shared drive via   Samba  to store your data, replace the value of    UPLOAD_LOCATION  with the path to your shared folder.   ✨   Tip:  If your CPU/iGPU/GPU supports it, Immich can use hardware acceleration for video playback and image recognition. This can triple performance. Learn more about   Transcoding  and   Machine Learning .  Deploy the container.  You're done! You can connect and follow the setup instructions at   http://yourserverip:2283 .  Exposing Immich with SWAG   The main benefit of this setup is being able to access Immich remotely on all your devices. We'll expose Immich using SWAG.   📋   Before you begin:   \nWe assume that you have a subdomain   immich.yourdomain.com  with a   CNAME  pointing to   yourdomain.com  in your   DNS zone . Also, unless you're using   Cloudflare Zero Trust , make sure port   443  on your router is forwarded to port   443  on your server via   NAT rules .  In Dockge, open the SWAG stack and edit the compose file to add Immich's network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :              # Connects the container to the custom network \n         # ...           \n         -   immich              # Network name defined in the stack\n       \n   networks  :                   # Defines the custom network\n     # ...\n     immich  :                   # Network name defined in the stack\n       name  :   immich_default    # Actual external network name\n       external  :   true          # Indicates it's an external network\n     We're assuming Immich's network is named   immich_default . You can check connectivity by visiting the SWAG dashboard at   http://yourserverip:81 .  Restart the stack by clicking \"deploy\" and wait for SWAG to fully initialize.  In the SWAG folders, create a file named   immich.subdomain.conf .      Tip:  You can use   File Browser  to navigate and edit your files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/immich.subdomain.conf\n  Press   i  to enter insert mode, then paste the following configuration:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   immich.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app immich_server;\n           set   $upstream_port   3001  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   \n       location   ~   (/immich)?/api   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app immich_server;\n           set   $upstream_port   3001  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Press   Esc , type   :x , then hit   Enter  to save and exit.  That's it! Immich is now accessible from the internet. Don’t forget to install the   iOS  /   Android  apps to sync your devices.   ✨   Tip:  You can protect this app with Authentik natively by   following these instructions .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":232,"path":233,"dir":226,"title":234,"description":7,"keywords":235,"body":237},"content:3.serveex:6.cloud:2.nextcloud.md","/serveex/cloud/nextcloud","Nextcloud",[106,236],"Exposing Nextcloud with Swag","   Nextcloud   🎯   Goals:  Install   Nextcloud  to manage your photos and files across all your devices.   Nextcloud  is a self-hosted solution that allows you to access and synchronize your data across all your devices. It also includes collaboration features, calendar, and more. It’s a great alternative to services like Google Drive, iCloud, or OneDrive.    Installation      We'll be using the Docker image maintained by   LinuxServer.io  File structure:     root\n   └──   docker\n       └──   nextcloud\n           ├──   config\n           ├──   data\n           ├──   compose.yaml\n           └──   .env\n  Open Dockge, click on   compose , name the stack   nextcloud  and paste the following:     ---\n   services  :\n     nextcloud  :\n       image  :   lscr.io/linuxserver/nextcloud:latest\n       container_name  :   nextcloud\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${GUID}\n         -   TZ=Etc/UTC\n       volumes  :\n         -   /docker/nextcloud/config:/config\n         -   /docker/nextcloud/data:/data\n       ports  :\n         -   ${PORT}:443\n       restart  :   unless-stopped\n     If you’re using a NAS or network-shared drive via   Samba , replace   /docker/nextcloud/data  with the path to your shared folder.  Find your   PUID  and   GUID  by running the following command:     id   username\n  Then fill out the   .env  file with your preferred port and the values found above, for example:     PUID  =1000\n   GUID  =1000\n   PORT  =4545\n  Deploy the stack and visit   http://yourserverip:4545  to complete the setup.      If it fails:  check your firewall rules.  Exposing Nextcloud with Swag   The goal of this setup is to access Nextcloud remotely from all your devices. We’ll use Swag to expose the app.     We assume you have a subdomain   nextcloud.yourdomain.com  with a   CNAME  pointing to   yourdomain.com  in your   DNS zone . And unless you’re using   Cloudflare Zero Trust , port   443  on your router must be forwarded to port   443  on your server using   NAT rules .  In Dockge, go to your SWAG stack and edit the compose to add Nextcloud's network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :               \n         # ...           \n         -   nextcloud              \n       \n   networks  :                    \n     # ...\n     nextcloud  :                 \n       name  :   nextcloud_default    \n       external  :   true\n     We assume the Nextcloud network is named   nextcloud_default . You can confirm connectivity by visiting the SWAG dashboard at   http://yourserverip:81 .  Redeploy the stack and wait for SWAG to become fully operational.  In Nextcloud’s files, edit the   config.php  file:   ✨   Tip:  You can use   File Browser  to navigate and edit files instead of using terminal commands.     sudo   vi   /docker/nextcloud/config/www/nextcloud/config/config.php\n  Enter edit mode with   i  and paste the following before the final   ); :     'trusted_proxies' => [gethostbyname('swag')],\n   'overwrite.cli.url' => 'https://nextcloud.example.com/',\n   'overwritehost' => 'nextcloud.example.com',\n   'overwriteprotocol' => 'https',\n  Also add your domain in the   array  section. It should look like this:     array (\n      0 => '192.168.0.1:444', # This line may differ—don’t change it!\n      1 => 'nextcloud.yourdomain.com', # Add your domain here\n   ),\n  Press   Esc , then save and exit by typing   :x  and hitting Enter.  In Swag’s folders, create the file   nextcloud.subdomain.conf :     sudo   vi   /docker/swag/config/nginx/proxy-confs/nextcloud.subdomain.conf\n  Enter edit mode with   i  and paste the following:     ## Version 2024/04/25\n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   nextcloud.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       location   /   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app nextcloud;\n           set   $upstream_port   443  ;\n           set   $upstream_proto https;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n           # Hide proxy response headers from Nextcloud that conflict with ssl.conf\n           proxy_hide_header   Referrer-Policy;\n           proxy_hide_header   X-Content-Type-Options;\n           proxy_hide_header   X-Frame-Options;\n           proxy_hide_header   X-XSS-Protection;\n   \n           # Disable proxy buffering\n           proxy_buffering   off  ;\n       }\n   }\n  Press   Esc , save and exit with   :x  then Enter.  That’s it—you’ve exposed Nextcloud! Don’t forget to install   the desktop and mobile apps .   ✨   Tip:  You can natively protect this app with Authentik by   following these instructions .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":239,"path":240,"dir":241,"title":242,"description":7,"keywords":243,"body":245},"content:3.serveex:7.files:1.file-browser.md","/serveex/files/file-browser","files","File Browser",[106,244],"Exposing File Browser with Swag","   File Browser   🎯   Objectives:   Install File Browser  Expose File Browser using Swag   File Browser  is a web-based interface that lets you access and edit the files on your server.    Installation   Open Dockge, click on   compose , name the stack   filebrowser , then copy and paste the following:     ---\n   services  :\n     filebrowser  :\n       container_name  :   filebrowser\n       volumes  :\n         -   /docker/filebrowser/config:/config/\n         -   /path/to/your/folders:/yourfolders   #add your folders to browse as /docker:/docker for exemple\n       ports  :\n         -   8010:80\n       image  :   filebrowser/filebrowser:s6\n   ✨   Tip:  Add the watchtower label to each container to automate updates.     services  :\n     filebrowser  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Deploy the container and go to   http://yourserverip:8010 . That’s it—your File Browser web UI is up and running!      If it doesn’t work:  check your firewall rules.  Exposing File Browser with Swag      File Browser does not support multi-factor authentication. Exposing it publicly could put your systems at risk. Only do this if you’re using a secure authentication solution like   Authentik . Otherwise, do not expose it with SWAG—use a VPN like   Wireguard  instead.  You may want to access File Browser remotely from all your devices. To do that, we’ll expose it through Swag.      Pre-requisite:  We assume you've already created a subdomain like   files.yourdomain.com  in your   DNS zone  pointing to   yourdomain.com  with a   CNAME , and—unless you're using Cloudflare Zero Trust—have already forwarded port   443  on your router to port   443  on your server using   NAT rules .  In Dockge, go to the SWAG stack and edit the compose file to add File Browser’s network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :                   # Connects the container to the custom network \n         # ...           \n         -   filebrowser              # Name of the network declared in the stack\n       \n   networks  :                        # Defines the custom network\n     # ...\n     filebrowser  :                   # Name of the network declared in the stack\n       name  :   filebrowser_default    # Actual name of the external network\n       external  :   true               # Specifies it's an external network\n     Here, we assume the network name for File Browser is   filebrowser_default . You can confirm the connection is working by accessing the SWAG dashboard at   http://yourserverip:81 .  Restart the stack by clicking \"deploy\" and wait for SWAG to fully initialize.  In the Swag folders, create the file   files.subdomain.conf .     sudo   vi   /docker/swag/config/nginx/proxy-confs/files.subdomain.conf\n  Enter insert mode by pressing   i , and paste the following configuration:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   files.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app filebrowser;\n           set   $upstream_port   80  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Press   Esc , then save and exit with   :x  followed by   Enter .  That’s it—File Browser is now exposed!   ✨   Tip:  You can protect this app with Authentik by opening   files.subdomain.conf  and uncommenting    include   /config/nginx/authentik-server.conf;  and    include   /config/nginx/authentik-location.conf; . Don’t forget to   create an application and provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":247,"path":248,"dir":241,"title":249,"description":7,"keywords":250,"body":253},"content:3.serveex:7.files:2.pingvin.md","/serveex/files/pingvin","Pingvin",[106,251,252],"Exposer Immich avec Swag","Sécuriser Pingvin avec Authentik","   Pingvin   🎯   Objectifs :   Installer Pingvin  Exposer Pingvin   Pingvin  est un outil permettant de partager rapidement des fichiers, à la manière de WeTransfer. Ses nombreuses options de partage (mot de passe, durée d'expiration, personnalisation du lien...) en font l'outil idéal pour partager rapidement des fichiers. Pingvin permet également de créer des   demandes de dépot , c'est à dire un lien partageable à envoyer à quelqu'un de votre choix pour qu'il puisse téléverser ses fichiers afin que vous puissiez les récupérer.    Installation   Ouvrez Dockge, cliquez sur   compose , appelez la stack   pingvin  puis copiez collez ceci :     ---\n   services  :\n     pingvin-share  :\n       container_name  :   pingvin\n       image  :   stonith404/pingvin-share\n       restart  :   unless-stopped\n       ports  :\n         -   3600:3000\n       volumes  :\n         -   /docker/pingvin/data:/opt/app/backend/data\n         -   /docker/pingvin/data/img:/opt/app/frontend/public/img\n         -   /docker/pingvin/uploads:/opt/app/backend/uploads   # chemin du dossier dans lequel vous souhaitez stocker les fichiers uploadés dans pingvin. A changer selon vos préférences.\n       depends_on  :\n         clamav  :\n           condition  :   service_healthy\n       networks  :\n         -   swag\n   \n     clamav  :   #antivirus pour les fichiers\n       restart  :   unless-stopped\n       image  :   clamav/clamav\n     Ici nous partons du principe que le nom du réseau de Swag est   swag_default .   ✨   Astuce :  ajoutez le label de watchtower dans chaque conteneur afin d'automatiser les mises à jour     services  :\n     filebrowser  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n     \n     clamav  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Déployez le conteneur et rendez-vous sur   http://ipduserveur:3600 . Et voilà, votre instance File Browser en webui est disponible !      En cas d'échec :  vérifiez les règles de votre pare-feu.  Exposer Immich avec Swag   Tout l'intérêt d'une telle solution, c'est de pouvoir y accéder à distance et sur tout vos appareils. Pour cela, nous allons exposer Pingvin via Swag.   📋   Au préalable :   \nNous partons du principe que vous avez le sous-domaine   pingvin.mondomaine.fr  avec un   CNAME  qui pointe vers   mondomaine.fr  dans votre   zone DNS . Et que bien sûr,   à moins que vous utilisiez Cloudflare Zero Trust , le port   443  de votre box pointe bien sur le port   443  de votre serveur via   les règles NAT .  Dans Dockge, rendez-vous dans la stack de SWAG et éditez le compose en ajoutant le réseau de pingvin :     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :               # Relie le conteneur au réseau custom \n         # ...           \n         -   pingvin              # Nom du réseau déclaré dans la stack\n       \n   networks  :                    # Définit le réseau custom\n     # ...\n     pingvin  :                   # Nom du réseau déclaré dans la stack\n       name  :   pingvin_default    # Nom véritable du réseau externe\n       external  :   true           # Précise que c'est un réseau à rechercher en externe\n     Ici nous partons du principe que le nom du réseau de pingvin est   pingvin_default . Vous pouvez vérifier que la connexion est opérationnelle en visitant le dashboard de SWAG en tapant   http://ipduserveur:81 .  Relancez la stack en cliquant sur \"déployer\" et patientez le temps que SWAG soit complètement opérationnel.  Dans les dossiers de Swag, créez le fichier   pingvin.subdomain.conf .      Astuce :  vous pouvez utiliser   File Browser  pour naviguer dans vos fichier et éditer vos documents au lieu d'utiliser les commandes du terminal.     sudo   vi   /docker/swag/config/nginx/proxy-confs/pingvin.subdomain.conf\n  Entrez en modification avec la touche   i  et collez la configuration ci-dessous :     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   pingvin.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app pingvin;\n           set   $upstream_port   3000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n  Appuyez sur   Echap puis sauvegardez et quittez en tapant  :x  puis en appuyant sur Entrée`.  Et voilà, vous avez exposé Pingvin !  Sécuriser Pingvin avec Authentik  Vous pouvez protéger cette app avec Authentik de façon native en suivant les instructions ci-dessous.   Dans votre espace admin authentik, créez un fournisseur OAuth2/OpenID.  Remplissez chaque section comme suit en remplaçant   mondomaine.fr  par votre domaine. Copiez quelque part le contenu des champs   ID du client  et   Secret du client .       Enregistrez et créez une application   pingvin  comme suit.     Enregistrez et aller dans la liste de vos avant-postes. Ajoutez le fournisseur pingvin` à votre avant-poste.  Quittez authentik, et allez dans l'interface d'administration de Pingvin.  Dans la section   « Identifiant social »  renseignez les champs suivant :    URI de découverte OpenID  avec   https://pingvin.mondomaine.fr/application/o/pingvin/.well-known/openid-configuration  (n'oubliez pas de remplacer   mondomaine.fr  par votre domaine)   Revendication du nom d’utilisateur OpenID  avec   preferred_username   ID du client OpenID  avec l'ID que vous avez copié en étape 2.   Secret du client OpenID  avec le token que vous avez copié en étape 2.  Et voilà, désormais lorsque vous vous connectez à Pingvin, un bouton \"Open ID\" sera disponible en dessous de la mire de connexion.  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":255,"path":256,"dir":257,"title":258,"description":7,"keywords":259,"body":262},"content:3.serveex:8.development:1.code-server.md","/serveex/development/code-server","development","Code-Server",[106,260,261],"Mount Folders","Expose code-server with Swag","   Code-Server   🎯   Goals:   Install code-server  Mount folders into VS Code  Expose code-server with Swag   code-server  is a container that lets you access   VS Code  via a web UI in a Linux environment. It's literally VS Code and your projects in your pocket, available anywhere.    Installation      For this setup, we’ll use the   image maintained by LinuxServer.io .  Folder structure     root\n   ├──   docker\n   │     └──   code-server\n   │         └──   config\n   └──   #any folder you want to mount in VS Code\n  Open Dockge, click on   compose , name the stack   code-server , and paste the following:     ---\n   services  :\n     code-server  :\n       image  :   lscr.io/linuxserver/code-server:latest\n       container_name  :   code-server\n       environment  :\n         -   PUID=${PUID}\n         -   PGID=${GUID}\n         -   TZ=Etc/UTC\n         -   HASHED_PASSWORD=${PW}\n       volumes  :\n         -   /docker/code-server/config:/config\n       # add folders to mount in VS Code\n       # - /path/to/folder:/folder\n       ports  :\n         -   8443:8443\n       restart  :   unless-stopped\n   ✨ Add the Watchtower label to each container to automate updates     services  :\n     code-server  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Choose a password and generate its hash:     echo   -n   \"yourpassword\"   |   npx   argon2-cli   -e\n  Save the result carefully. Find your PUID and GUID with:     id   yourusername\n  Fill in the   .env  file with the values you found, for example:     PW  =  '$argon2i$v=19$m=4096,t=3,p=1$wST5QhBgk2lu1ih4DMuxvg$LS1alrVdIWtvZHwnzCM1DUGg+5DTO3Dt1d5v9XtLws4'\n   PUID  =1000\n   GUID  =1000\n      Note:  Make sure to wrap the hash in single quotes   '  Deploy the container and go to   http://yourserverip:8443 . Voilà, your code-server instance is up and running in the browser!      If it fails:  check your firewall rules.  Mount Folders   You can mount folders into VS Code by adding the relevant volumes in   compose.yaml  (or via Dockge), then redeploy the container.     services  :\n     code-server  :\n       #...\n       volumes  :\n         -   /path/to/folder:/folder\n  Once inside VS Code, you'll have access to the mounted folder.  Expose code-server with Swag   The whole point of such a solution is to access it remotely from any device. To do this, we’ll expose code-server via Swag.      Preliminary:  We assume you’ve created a subdomain like   code.yourdomain.com  with a   CNAME  pointing to   yourdomain.com  in your   DNS zone , and—unless you're using   Cloudflare Zero Trust —that you’ve forwarded port   443  from your router to port   443  on your server using   NAT rules .  In Dockge, go to the SWAG stack and edit the compose file to add code-server’s network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :             # Connects the container to a custom network \n         # ...           \n         -   code-server        # Name of the network defined in the stack\n       \n   networks  :                  # Defines the custom network\n     # ...\n     code-server  :             # Name of the network defined in the stack\n       name  :   code-serveur     # Actual name of the external network\n       external  :   true         # Indicates it’s an external network\n     We assume the network name is   code-server_default . You can verify that the connection works by visiting the SWAG dashboard at   http://yourserverip:81 .  Redeploy the stack by clicking “deploy” and wait until SWAG is fully operational.  Inside the Swag config folders, create the file   code.subdomain.conf .   ✨   Tip:  You can use   File Browser  to navigate and edit your files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/code.subdomain.conf\n  Enter insert mode with   i  and paste the following configuration:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   code.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app code-server;\n           set   $upstream_port   8443  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Press   Esc , then save and exit by typing   :x  and pressing   Enter .  That’s it — code-server is now exposed!   ✨   Tip:  You can protect this app with Authentik by opening   code.subdomain.conf  and uncommenting the lines   include /config/nginx/authentik-server.conf;  and   include /config/nginx/authentik-location.conf; . Don’t forget to   create an application and provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":264,"path":265,"dir":257,"title":266,"description":7,"keywords":267,"body":269},"content:3.serveex:8.development:2.gitea.md","/serveex/development/gitea","Gitea",[106,268],"Exposing Gitea with Swag","   Gitea   🎯   Goals:   Install Gitea  Expose Gitea using Swag   Gitea  is a self-hosted DevOps platform that allows you to manage repositories much like GitHub, but on your own infrastructure.    Installation   Folder structure     root\n   └──   docker\n       └──   gitea\n           └──   data\n  Open Dockge, click on   compose , name the stack   gitea , and paste the following content:     ---\n   networks  :\n     gitea  :\n       external  :   false\n   services  :\n     server  :\n       image  :   gitea/gitea:1.22.0\n       container_name  :   gitea\n       environment  :\n         -   USER_UID=${UID}\n         -   USER_GID=${GID}\n         -   TZ=Europe/Paris\n       restart  :   unless-stopped\n       networks  :\n         -   gitea\n       volumes  :\n         -   ./data:/data\n       ports  :\n         -   3333:3000\n         -   222:22\n  Fill out the   .env  file with the required information, for example:     UID  =1000\n   GID  =1000\n  Deploy the container and go to   http://yourserverip:3333 . Your Gitea instance is now up and running!      If it fails:  check your firewall rules.  Exposing Gitea with Swag   The benefit of this setup is being able to access it remotely from any of your devices. To do so, we’ll expose Gitea through Swag.      Prerequisite:  We assume you have created a subdomain such as   gitea.yourdomain.com  in your   DNS zone  with   CNAME  pointing to   yourdomain.com , and   unless you're using Cloudflare Zero Trust , you have already forwarded port   443  from your router to your server’s port   443  in the   NAT rules .  In Dockge, go to the SWAG stack and edit the compose file by adding Gitea's network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :             # Connect the container to the custom network\n         # ...           \n         -   gitea              # Name of the declared network\n       \n   networks  :                  # Define the custom network\n     # ...\n     gitea  :                   # Name of the declared network\n       name  :   gitea_default    # Actual external network name\n       external  :   true         # Indicates it's an external network\n     We assume the Gitea network name is   gitea_default . You can verify connectivity by visiting the SWAG dashboard at   http://yourserverip:81 .  Redeploy the stack by clicking \"Deploy\" and wait until SWAG is fully operational.  Inside the Swag folders, create the file   gitea.subdomain.conf .   ✨   Tip:  You can use   File Browser  to navigate and edit your files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/gitea.subdomain.conf\n  Press   i  to enter edit mode and paste the configuration below:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   gitea.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app gitea;\n           set   $upstream_port   3000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   \n       location   ~   (/gitea)?/info/lfs   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app gitea;\n           set   $upstream_port   3000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n  Press   Esc , then save and exit by typing   :x  and hitting   Enter .  Now open the   app.ini  file from the container's file system:     sudo   vi   /docker/gitea/data/gitea/conf/app.ini\n  Press   i  to edit, then modify the server section with your domain information:     [server]\n   DOMAIN   = gitea.yourdomain.com\n   SSH_DOMAIN   = gitea.yourdomain.com\n   ROOT_URL   = https://gitea.yourdomain.com/\n  Press   Esc , save and exit with   :x , then restart the container.  And that’s it! Gitea is now exposed to the web.   ✨   Tip:  You can natively protect this app with Authentik by   following these instructions .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":271,"path":272,"dir":257,"title":273,"description":7,"keywords":274,"body":276},"content:3.serveex:8.development:3.it-tools.md","/serveex/development/it-tools","IT Tools",[106,275],"Expose IT Tools with Swag","   IT Tools   🎯   Goals:   Install IT Tools  Expose IT Tools with Swag   IT Tools  is a container exposing a web page that provides access to a wide range of development tools.    Installation   Open Dockge, click on   compose , name the stack   it-tools , and paste the following:     ---\n   services  :\n     it-tools  :\n       container_name  :   it-tools\n       restart  :   unless-stopped\n       image  :   corentinth/it-tools:latest\n       ports  :\n         -   3222:80\n   ✨   Tip:  Add the Watchtower label to each container to enable automatic updates.     services  :\n     it-tools  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Deploy the container and visit   http://yourserverip:3222 . That’s it, your IT Tools web UI instance is up and running!      If it fails:  check your firewall rules.  Expose IT Tools with Swag   You might want to access it remotely on all your devices. To do that, we'll expose IT Tools using Swag.      Pre-requisite:  We assume you’ve created a subdomain like   tools.yourdomain.com  in your   DNS zone  with   CNAME  set to   yourdomain.com . Also, unless you’re using   Cloudflare Zero Trust , make sure you’ve already forwarded port   443  from your router to port   443  on your server in the   NAT rules .  In Dockge, go to the SWAG stack and edit the compose file to add the IT Tools network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ...\n        networks  :                # Connects the container to the custom network \n         # ...           \n         -   it-tools              # Network name as defined in the IT Tools stack\n       \n   networks  :                     # Defines the custom network\n     # ...\n     it-tools  :                   # Network name as defined in the IT Tools stack\n       name  :   it-tools_default    # Actual name of the external network\n       external  :   true            # Indicates it's an external network\n     We assume the IT Tools network is named   it-tools_default . You can check connectivity by visiting the SWAG dashboard at   http://yourserverip:81 .     We also assume the SWAG network is named   swag_default .  Restart the stack by clicking \"deploy\" and wait for SWAG to be fully operational.  Inside the Swag folders, create the file   tools.subdomain.conf .   ✨   Tip:  You can use   File Browser  to navigate and edit your files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/tools.subdomain.conf\n  Enter edit mode by pressing   i  and paste the configuration below:     ## Version 2023/12/19\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   tools.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app it-tools;\n           set   $upstream_port   80  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n  Press   Esc , then save and exit by typing   :x  and pressing   Enter .  And that’s it — IT Tools is now exposed!   ✨   Tip:  You can secure this app with Authentik by opening   tools.subdomain.conf  and uncommenting the lines   include /config/nginx/authentik-server.conf;  and   include /config/nginx/authentik-location.conf; . Don’t forget to   create an application and a provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":278,"path":279,"dir":280,"title":281,"description":7,"keywords":282,"body":287},"content:3.serveex:9.apps:1.adguard.md","/serveex/apps/adguard","apps","Adguard Home",[106,283,284,285,286],"Exposing AdGuard with SWAG","Configure SSL/TLS Encryption","Configure Devices","Add Filters","   Adguard Home   🎯   Goals:   Install and deploy Adguard  Expose Adguard  Secure DNS queries with SSL/TLS  Configure client devices   AdGuard Home  is a DNS server that blocks ads and tracking at the system level. Once configured, it will protect ALL your home devices without the need for any client-side software.  It works as a DNS server that redirects tracking domains to a “black hole,” preventing your devices from connecting to them.  In practice, once it's in place, all you need to do is set your devices to use Adguard as their DNS server.   Quick reminder of how DNS works:  When you visit a site or use an app, it makes requests to various domains to load content—ads in particular. Your device doesn’t know the IP addresses of these domains, so it contacts a   Domain Name Server  (DNS), which returns the current IP address.  By default, your device uses your ISP's DNS server, which is usually configured in your router or, for mobile devices, at the carrier’s CGNAT level. You can change this in your browser settings, your device’s system settings, or even directly in your router, depending on your ISP.  Adguard will act as a middleman between your device and the upstream DNS servers. If you configure your devices to use Adguard:   If the domain is not in a blocklist, Adguard queries the upstream DNS servers and returns the correct IP to your device.  If the domain   is  in a blocklist, Adguard will block the request and return nothing, so the associated content won’t load.  This is how ads and malicious domains are blocked—Adguard blocks only the bad domains, allowing the rest of the page to load normally.    Installation   Folder structure:     root\n   └──   docker\n       └──   adguard\n           ├──   confdir\n           ├──   workdir\n           ├──   compose.yaml\n           └──   .env\n     We will also mount the   /docker/swag/config/etc/letsencrypt  folder to access Swag's SSL certificate.  Open Dockge and click   compose  Name the stack   adguardhome  and paste the configuration below:     ---\n   services  :\n     adguardhome  :\n       container_name  :   adguard\n       image  :   adguard/adguardhome\n       restart  :   unless-stopped\n       ports  :\n         -   53:53/udp\n         -   8080:80/tcp\n         -   4443:443/tcp\n         -   853:853/tcp\n         -   3000:3000/tcp\n       volumes  :\n         -   /docker/adguardhome/confdir:/opt/adguardhome/conf\n         -   /docker/adguardhome/workdir:/opt/adguardhome/work\n         -   /docker/swag/config/etc/letsencrypt:/swag-ssl:ro\n   ✨   Tip:  Add the watchtower label to each container to automate updates     services  :\n     adguardhome  :\n       # ...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Deploy the stack.  Go to   http://yourserverip:3000  and follow the setup instructions.  That’s it! Adguard is deployed.  Exposing AdGuard with SWAG   To make AdGuard usable from outside your home network, you need to expose it.      Prerequisites:  We assume you've created a subdomain like   adguard.mydomain.com  in your   DNS zone  with a   CNAME  pointing to   mydomain.com , and that you’ve already forwarded port   443  from your router to port   443  on your server in your   NAT rules . Also forward port   53  and port   853  to your server. These ports are used to route DNS requests.     Do not use Cloudflare tunnels to expose AdGuard, and make sure any proxying is disabled.  In Dockge, go to the SWAG stack and edit the compose file to add the AdGuard network:     services  :\n     swag  :\n        container_name  :   # ...\n         # ... \n        networks  :               # Connect the container to the custom network \n         # ...           \n         -   adguard              # Name of the network declared in the stack\n       \n   networks  :                    # Define the custom network\n     # ...\n     adguard  :                   # Name of the network declared in the stack\n       name  :   adguard_default    # Actual name of the external network\n       external  :   true           # Specifies that this is an external network\n     We assume here that the AdGuard network is named   adguard_default . You can verify the connection is working by visiting the SWAG dashboard at   http://yourserverip:81 .  Restart the stack by clicking \"Deploy\" and wait for SWAG to be fully operational.  Create and open the file   adguard.subdomain.conf   ✨   Tip for terminal haters: \nYou can use   File Browser  to browse and edit files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/adguard.subdomain.conf\n  Edit the file by pressing   i  and then pasting the configuration below:     ## Version 2023/05/31\n   # make sure that your adguard container is named adguard\n   # make sure that your dns has a cname set for adguard\n   \n   server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   adguard.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   0  ;\n   \n       #if ($lan-ip = yes) { set $geo-whitelist yes; }\n       #if ($geo-whitelist = no) { return 404; }\n       if   ($geo-blacklist   =   no) {   return   404  ; }\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth (requires ldap-server.conf in the server block)\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia (requires authelia-server.conf in the server block)\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik (requires authentik-server.conf in the server block)\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app adguard;\n           set   $upstream_port   3000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   \n       location   /control   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app adguard;\n           set   $upstream_port   3000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   \n       location   /dns-query   {\n           # to properly use this please set `allow_unencrypted_doh: true` and `force_https: false` in adguard\n           # see https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration#configuration-file\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app adguard;\n           set   $upstream_port   3000  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n   \n       }\n   }\n   ✨   Tip:   \nYou can protect this app with Authentik by opening   adguard.subdomain.conf  and removing the   #  in front of    include   /config/nginx/authentik-server.conf;  and    include   /config/nginx/authentik-location.conf; . Don’t forget to   create an application and a provider in Authentik . You’ll need to exclude the URL   https://adguard.mydomain.com/dns-query  from authentication:   Edit the AdGuard provider  Under   Advanced Protocol Settings > Authenticated Paths , enter   ^/dns-query  Press   Esc , then save and exit by typing   :x  And that's it! AdGuard is now exposed!  Configure SSL/TLS Encryption   Encryption is essential if you want to keep your queries to AdGuard private. Encrypting your queries ensures that no one—not even your ISP—can see your history. It also ensures that only your server can respond to you.  To configure encryption:   Go to   Settings  then   Encryption .  Set the options as follows:     Below, in the   Certificates  section, check   Use file path for certificate  In the input field, enter   /swag-ssl/live/mydomain.com/fullchain.pem , replacing   mydomain.com  with your actual domain.  For   Private Key , check   Use file path for private key  In the input field, enter   /swag-ssl/live/mydomain.com/privkey.pem , replacing   mydomain.com  accordingly.  Save  Done! Your future DNS queries are now protected!  Configure Devices   You have several options (which you can combine) to configure your devices.  Secure the Local Network  You can secure your local network with AdGuard by configuring your router to direct all DNS queries by default to AdGuard instead of your ISP’s DNS. Note: your router must allow DNS changes (Orange routers do not).  This option is usually in the   DHCP  settings. Make sure to add a fallback DNS server such as:   Cloudflare:   1.1.1.1  Google:   8.8.8.8  Without this, if your server goes down, your devices will lose internet access.     Some devices may have a separate DNS configured and may not use the router’s DNS.  Force a Browser to Use AdGuard  In your browser, you can configure a DNS to force it to use AdGuard Home.\nIn the settings, specify the address   https://adguard.mydomain.com/dns-query  Configure AdGuard at the System Level on Windows  In Windows, you need to configure AdGuard for each network adapter you want to use.   Go to   Home > Network & Internet , then select the network adapter to modify  Click   Edit DNS  (sometimes under   Hardware Properties )  Choose   Manual  Enable IPv4  Enter your server’s public IP (the one accessible from the internet)  Enable   DNS over HTTPS (manual template)  Disable   Fallback to plaintext  Save  All programs using that network adapter will now be filtered by AdGuard.  Add Filters    Go to the settings and change the filters.  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":289,"path":290,"dir":280,"title":291,"description":7,"keywords":292,"body":294},"content:3.serveex:9.apps:2.vaultwarden.md","/serveex/apps/vaultwarden","Vaultwarden",[106,293],"Exposing Vaultwarden with SWAG","   Vaultwarden   🎯   Goals:  Install   Vaultwarden  to manage your passwords across all your devices (a replacement for Google or Apple password managers).     Vaultwarden  is a password management solution (generation, autofill...) that you can host directly on your server. This replaces managers like Google, Apple, or Keepass. Vaultwarden synchronizes your passwords across all your devices with end-to-end encryption.  Vaultwarden is a fork of   Bitwarden .  Installation   Folder structure:     root\n   └──   docker\n       └──   vaultwarden\n           ├──   data\n           ├──   compose.yaml\n           └──   .env\n  Open Dockge, click on   compose , name the stack   vaultwarden , and paste the following:     ---\n   services  :\n     vaultwarden  :\n       container_name  :   vaultwarden\n       image  :   vaultwarden/server:latest\n       restart  :   unless-stopped\n       env_file  :\n         -   .env\n       volumes  :\n         -   ./data/:/data/\n       ports  :\n         -   3050:80\n       environment  :\n         -   DOMAIN=${URL}\n         -   LOGIN_RATELIMIT_MAX_BURST=10\n         -   LOGIN_RATELIMIT_SECONDS=60\n         -   ADMIN_RATELIMIT_MAX_BURST=10\n         -   ADMIN_RATELIMIT_SECONDS=60\n         -   ADMIN_TOKEN=${TOKEN}\n         -   SENDS_ALLOWED=true\n         -   EMERGENCY_ACCESS_ALLOWED=true\n         -   WEB_VAULT_ENABLED=true\n         -   SIGNUPS_ALLOWED=false\n         -   SIGNUPS_VERIFY=true\n         -   SIGNUPS_VERIFY_RESEND_TIME=3600\n         -   SIGNUPS_VERIFY_RESEND_LIMIT=5\n   ✨   Tip:  Add the Watchtower label in each container to automate updates     services  :\n     vaultwarden  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  Next, generate a password hash to put in the   TOKEN  variable in   .env :     echo   -n   'yourpassword'   |   argon2   \"$(  openssl   rand   -base64   32  )\"   -e   -id   -k   65540   -t   3   -p   4\n  Copy the result securely.  In the   .env  file, enter the following variables:     URL  =\n   TOKEN  =\n     Variable  Value  Example     URL  The URL of your Vaultwarden server   https://vault.yourdomain.com    TOKEN  The token you previously copied   '$argon2id$v=19$m=65540,t=3,p=4$bXBGME...  Then deploy the container.  Recently, Vaultwarden requires SSL to be accessed, which prevents access via a local IP. We'll expose it with SWAG, which provides an SSL certificate.      If it fails:  check your firewall rules.  Exposing Vaultwarden with SWAG   The main benefit of Vaultwarden is being able to access it remotely from any device. We'll expose it through   SWAG .   ✨   Before you start:  Make sure you've created a DNS subdomain like   vault.yourdomain.com  with   CNAME  pointing to   yourdomain.com  and (unless using Cloudflare Zero Trust) that you've forwarded port   443  from your router to your server's   443  via   NAT rules .  In Dockge, go to the SWAG stack and edit the compose file to add the Vaultwarden network:     services  :\n     swag  :\n        container_name  :   # ...\n        # ...\n        networks  :               # Connects container to custom network\n         # ...\n         -   vaultwarden          # Name of the declared network\n       \n   networks  :                    # Defines the custom network\n     # ...\n     vaultwarden  :               # Name of the declared network\n       name  :   vaultwarden_default    # Actual name of the external network\n       external  :   true\n     We're assuming the network name is   vaultwarden_default . You can check connectivity by visiting the SWAG dashboard at   http://yourserverip:81 .  Restart the stack by clicking \"Deploy\" and wait for SWAG to be fully operational.  In SWAG's config folder, create the file   vault.subdomain.conf :   ✨   Tip:  Use   File Browser  to navigate and edit files instead of using terminal commands.     sudo   vi   /docker/swag/config/nginx/proxy-confs/vault.subdomain.conf\n  Press   i  to edit, and paste the following configuration:     server   {\n       listen   443   ssl;\n       listen   [::]:443 ssl;\n   \n       server_name   vault.*;\n   \n       include   /config/nginx/ssl.conf;\n   \n       client_max_body_size   128M  ;\n   \n       # enable for ldap auth (requires ldap-location.conf in the location block)\n       #include /config/nginx/ldap-server.conf;\n   \n       # enable for Authelia (requires authelia-location.conf in the location block)\n       #include /config/nginx/authelia-server.conf;\n   \n       # enable for Authentik (requires authentik-location.conf in the location block)\n       #include /config/nginx/authentik-server.conf;\n   \n       location   /   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app vaultwarden;\n           set   $upstream_port   80  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   \n       location   ~   ^(/vaultwarden)?/admin   {\n           # enable the next two lines for http auth\n           #auth_basic \"Restricted\";\n           #auth_basic_user_file /config/nginx/.htpasswd;\n   \n           # enable for ldap auth\n           #include /config/nginx/ldap-location.conf;\n   \n           # enable for Authelia\n           #include /config/nginx/authelia-location.conf;\n   \n           # enable for Authentik\n           #include /config/nginx/authentik-location.conf;\n   \n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app vaultwarden;\n           set   $upstream_port   80  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   \n       location   ~   (/vaultwarden)?/api   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app vaultwarden;\n           set   $upstream_port   80  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   \n       location   ~   (/vaultwarden)?/notifications/hub   {\n           include   /config/nginx/proxy.conf;\n           include   /config/nginx/resolver.conf;\n           set   $upstream_app vaultwarden;\n           set   $upstream_port   80  ;\n           set   $upstream_proto http;\n           proxy_pass   $upstream_proto://$upstream_app:$upstream_port;\n       }\n   }\n  Press   Esc , then type   :x  and press   Enter  to save and exit.  And there you go — Vaultwarden is now exposed! Visit   https://vault.yourdomain.com/admin  to access the admin panel and paste the password you specified when generatique the   ADMIN_TOKEN . For more information, see the   Bitwarden documentation .  Don't forget to install Bitwarden browser extensions (they work with Vaultwarden) for   Chrome  and   Firefox , as well as   iOS  and   Android  apps to sync your passwords.   ✨   Tip:  You can protect this app with Authentik by opening   tools.subdomain.conf  and removing the   #  in front of   include /config/nginx/authentik-server.conf;  and   include /config/nginx/authentik-location.conf; . Don't forget to   create an application and provider in Authentik .  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":296,"path":297,"dir":298,"title":34,"description":7,"keywords":299,"body":300},"content:4.stockeex:1.introduction.md","/stockeex/introduction","stockeex",[],"  Stockeex    \n   ",{"id":302,"path":303,"dir":304,"title":305,"description":7,"keywords":306,"body":307},"content:5.nonsense:1.python:1.nvidia-stock-bot.md","/nonsense/python/nvidia-stock-bot","python","Nvidia Stock Bot",[],"   🤖 Nvidia Stock Bot   For the past four years, the electronics hardware shortage has been relentless. Graphics cards are no exception. In 2020, I had to wait two months to get my RTX 3080. To manage it, I joined   JV Hardware , where a small group of geeks had set up a bot that pinged users when GPUs became available.  Four years later and with 5,000 members on the server, the RTX 5000 series is being released. Yet, no working stock bot seems to exist. Not to mention a certain “influencer” who charges users for access to a bot that doesn’t even work. He manually copies alerts from other servers like ours, which have already solved the issue.  Anyway, eager to get an RTX 5090 for my AI-dedicated machine, I decided it was time to dive into Python—with a little help from ChatGPT. Along with another member, KevOut, who helped guide me through the APIs and initial architecture, I ended up building a clean and functional bot that sends different kinds of Discord alerts—all deployable in a simple Docker container.  After many setbacks, I went from this:    To this:    And more recently :    And I was also lucky enough to be referenced in the famous   selfhost newsletter  !  More info directly on the repo:    🐋   Nvidia Stock Bot    Nvidia GPU stock alert bot",{"id":309,"path":310,"dir":304,"title":311,"description":7,"keywords":312,"body":313},"content:5.nonsense:1.python:2. adguard-cidre.md","/nonsense/python/adguard-cidre","Adguard CIDRE",[],"   🤖 Adguard CIDRE Sync   Adguard Home is a fantastic solution for DNS-level ad blocking and rewriting requests—perfect for removing ISP DNS trackers or intrusive ads.  It works great locally, but if you want all your devices (even on the go) to benefit, you’ll need to expose Adguard to the internet. Unfortunately, that means anyone can use it, potentially overloading your €1/month remote VPS.  Adguard allows whitelisting or blacklisting clients. The problem? To whitelist a client, you need their IP—but for mobile phones, that IP changes often. Instead of trying to whitelist ever-changing IPs, the better approach is to block broader IP ranges by region.  CIDRE is a tool that syncs geo-targeted IP ranges with firewalls. Instead of running CIDRE with a full firewall stack on the remote server, I figured I could just import those regularly updated IP ranges into Adguard’s blocklist.  Thus, Adguard CIDRE Sync was born: a container that syncs Adguard’s blocklist with CIDRE’s updated IP ranges on a schedule of your choosing.  The idea is to:   Backup Adguard’s config file on first run (original untouched version saved)  Download selected country IP ranges via an environment variable  Let you manually add custom IPs via a file  Concatenate, backup the config again (as the updated version), and inject the list into the correct blocklist section  Reload Adguard by restarting the container (using Docker socket proxy for limited permissions)  All fully autonomous, with frequency set via environment variable in the   docker-compose  config.  More info directly on the repo:    🐋   Adguard CIDRE Sync    Adguard blocklist sync bot",{"id":315,"path":316,"dir":304,"title":317,"description":7,"keywords":318,"body":319},"content:5.nonsense:1.python:3.lumeex.md","/nonsense/python/lumeex","Lumeex",[],"   \n      Yet another minimalist, lightweight photo gallery static site generator.  \n      Amateur photographer that I am, I spent several weeks looking for a framework with a photo gallery that could outshine Instagram. I wanted something that showcased the photos rather than the author, and that made every visit unique by loading the images in random order—while still allowing filtering and sorting by tag or combinations of tags.  In the end, I found nothing that did exactly what I wanted. And when something came close, it was always through heavy, bloated CMS platforms. So I decided to make a static site by hand, the old-school way, with Notepad++. Being fairly comfortable with HTML/CSS and a bit of JavaScript, I quickly came up with something nice during my vacation, between beach sessions. After all, a good craftsman should have good tools—and there’s no better tool than one you make yourself.  Then I thought it might be a good idea to automate certain tasks—like generating favicon formats, resizing and converting images, creating the gallery automatically instead of entering everything by hand, and generating   robots.txt  and   sitemap  files… so I turned back to Python.  Eventually, after getting good results, I figured I might as well go all the way: build a complete framework for generating a static site photo gallery, where all you need to do is fill in your site’s information in a config file and tweak the visuals a bit—without touching the code.  That’s how   Lum  eex  was born.  \n        Et voilà!       Documentation    Check out the doc     Repository    See the repo     Demo    Explore the demo",{"id":321,"path":322,"dir":304,"title":323,"description":7,"keywords":324,"body":325},"content:5.nonsense:1.python:4.instameex.md","/nonsense/python/instameex","Instameex",[],"   \n      Mix your SDR and HDR exports into an Instagram-ready HDR photo.  \n     Nothing is more frustrating than Instagram's HDR handling. It compresses and destroys gain maps, and the slightest change in aspect ratio or size simply strips HDR out entirely. As for Lightroom, its \"SDR preview\" system is frankly unacceptable, it makes it impossible to get consistent results. Until now, posting on Instagram meant choosing between decent SDR with broken HDR, or the other way around.  Why not simply edit your SDR file to perfection on one side, your HDR file on the other, and then recalculate a gain map from those two perfect files?\nA few pioneers have already gone down that road, notably with an   Adobe Lightroom Classic  plugin. Judge me if you want, but I only use Lightroom CC, which does not support plugins.  I drew inspiration from a   fork of the original project , the one that eventually became the LrC plugin, to build a frontend that can be easily deployed with Docker. Let's be honest: it was also a great excuse to put my Claude Code subscription to the test. And I have to say, watching it spin up its own environments, run end-to-end tests, self-correct its code, and write detailed summaries is genuinely impressive. I still reviewed everything myself, don't worry. I also learned a great deal about HDR fundamentals, gain maps, HLG/PQ tone curves, color spaces, and more.  In short, here is what my workflow now looks like for posting on Instagram:    Allow me to introduce   Instam  eex     And here is the result      🐋   Instameex    Open the repository    🌍   Online version    Convert online",{"id":327,"path":328,"dir":329,"title":330,"description":7,"keywords":331,"body":332},"content:5.nonsense:2.bash:1.servarr-duplicates.md","/nonsense/bash/servarr-duplicates","bash","Servarr corrector",[],"   Servarr duplicates corrector   Six months after downloading terabytes of media, I realized that Sonarr and Radarr were copying them into my Plex library instead of creating hardlinks. This happens due to a counterintuitive mechanism: if you mount multiple folders in Sonarr/Radarr, it sees them as different filesystems and thus cannot create hardlinks. That’s why you should mount only one parent folder containing all child folders (like   downloads ,   movies ,   tvseries  inside a   media  parent folder).  So I restructured my directories, manually updated every path in Qbittorrent, Plex, and others. The last challenge was finding a way to detect existing duplicates, delete them, and automatically create hardlinks instead—to save space.  My directory structure:     .\n   └──   media\n       ├──   seedbox\n       ├──   radarr\n       │     └──   tv-radarr\n       ├──   movies\n       └──   tvseries\n  The originals are in   seedbox  and must not be modified to keep seeding. The copies (duplicates) are in   movies  and   tvseries . To complicate things, there are also unique originals in   movies  and   tvseries . And within those, there can be subfolders, sub-subfolders, etc.  So the idea is to:   list the originals in seedbox  list files in movies and tvseries  compare both lists and isolate duplicates  delete the duplicates  hardlink the originals to the deleted duplicate paths  Yes, I asked ChatGPT and Qwen3 (which I host on a dedicated AI machine). Naturally, they suggested tools like rfind, rdfind, dupes, rdupes, rmlint... But hashing 30TB of media would take days, so I gave up quickly.  In the end, I only needed to find   .mkv  files, and duplicates have the exact same name as the originals, which simplifies things a lot. A simple Bash script would do the job.  Spare you the endless Q&A with ChatGPT—I was disappointed. Qwen3 was much cleaner. ChatGPT kept pushing awk-based solutions, which fail on paths with spaces. With Qwen’s help and dropping awk, the results improved significantly.  To test, I first asked for a script that only lists and compares:     #!/bin/bash\n   \n   # Create an associative array to store duplicates\n   declare   -A   seen\n   \n   # Find all .mkv files only (exclude directories)\n   find   /media/seedbox   /media/movies   /media/tvseries   -type   f   -name   \"*.mkv\"   -print0   |   \\\n   while   IFS  =   read   -r   -d   ''   file  ;   do\n       # Get the file's inode and name\n       inode  =  $(  stat   --format=  \"%i\"   \"  $file  \"  )\n       filename  =  $(  basename   \"  $file  \"  )\n       \n       # If the filename has been seen before\n       if   [[   -n   \"${  seen  [  $filename  ]}\"   ]];   then\n           # Check if the inode is different from the previous one\n           if   [[   \"${  seen  [  $filename  ]}\"   !=   \"  $inode  \"   ]];   then\n               # Output the duplicates with full paths\n               echo   \"Duplicates for   \\\"  $filename  \\\"  :\"\n               echo   \"${  seen  [\"  $filename  \"]} ${  seen  [\"  $filename  :full_path\"]}\"\n               echo   \"  $inode   $file  \"\n               echo\n           fi\n       else\n           seen[$filename]  =  \"  $inode  \"\n           seen[  \"  $filename  :full_path\"  ]  =  \"  $file  \"\n       fi\n   done\n  This gave me outputs like:   Duplicates for \"episode1.mkv\":\n1234567 /media/seedbox/sonarr/Serie 1/Season1/episode1.mkv\n2345678 /media/tvseries/Serie 1/Season1/episode1.mkv\n  With   awk , it would’ve stopped at   /media/seedbox/sonarr/Serie . I’m far from an expert, but Qwen3 performed better and explained everything clearly.  Once I verified the output, I asked for a complete script: compare, delete duplicates, create hardlinks.  Again, ChatGPT disappointed. Despite my requests, it created hardlinks   before  deleting the duplicates—effectively linking and then deleting the link (though the original is kept). Not helpful.  Quick stopover to Qwen3, RTX 5090 in overdrive, and bam—much better result. Yes, it kept ChatGPT-style emojis, but here it is:     #!/bin/bash\n   \n   echo   \"🔍 Step 1: Indexing original files in /media/seedbox...\"\n   declare   -A   seen\n   \n   # Index all .mkv files in seedbox\n   while   IFS  =   read   -r   -d   ''   file  ;   do\n       filename  =  $(  basename   \"  $file  \"  )\n       seen[  \"  $filename  \"  ]  =  \"  $file  \"\n   done   \u003C   \u003C(  find   /media/seedbox   -type   f   -name   \"*.mkv\"   -print0  )\n   \n   echo   \"📦 Step 2: Automatically replacing duplicates...\"\n   total_doublons  =  0\n   total_ko_saved  =  0\n   \n   while   IFS  =   read   -r   -d   ''   file  ;   do\n       filename  =  $(  basename   \"  $file  \"  )\n       original  =  \"${  seen  [  $filename  ]}\"\n   \n       if   [[   -n   \"  $original  \"   &&   \"  $original  \"   !=   \"  $file  \"   ]];   then\n           inode_orig  =  $(  stat   -c   %i   \"  $original  \"  )\n           inode_dupe  =  $(  stat   -c   %i   \"  $file  \"  )\n   \n           if   [[   \"  $inode_orig  \"   !=   \"  $inode_dupe  \"   ]];   then\n               size_kb  =  $(  du   -k   \"  $file  \"   |   cut   -f1  )\n               echo   \"🔁 Replacing:\"\n               echo   \"    Duplicate :   $file  \"\n               echo   \"    Original  :   $original  \"\n               echo   \"    Size      : ${  size_kb  } KB\"\n   \n               rm   \"  $file  \"   &&   ln   \"  $original  \"   \"  $file  \"   &&   echo   \"✅ Hardlink created.\"\n   \n               total_doublons  =  $((  total_doublons   +   1  ))\n               total_ko_saved  =  $((  total_ko_saved   +   size_kb  ))\n           fi\n       fi\n   done   \u003C   \u003C(  find   /media/movies /media/tvseries   -type   f   -name   \"*.mkv\"   -print0  )\n   \n   echo   \"\"\n   echo   \"🧾 Summary:\"\n   echo   \"    🔗 Duplicates replaced by hardlink:   $total_doublons  \"\n   echo   \"    💾 Approx. disk space saved: ${  total_ko_saved  } KB (~$((  total_ko_saved   /   1024  )) MB)\"\n   echo   \"✅ Done.\"\n  So, in conclusion, I:   Learned many Bash subtleties  Learned never to blindly copy-paste a ChatGPT script without understanding and dry-running it  Learned that Qwen on a RTX 5090 is more coherent than ChatGPT-4o on server farms (not even mentioning “normal” ChatGPT)  Learned that even with 100TB of storage, monitoring it would’ve alerted me much earlier to the 12TB of duplicates lying around  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":334,"path":335,"dir":329,"title":336,"description":7,"keywords":337,"body":338},"content:5.nonsense:2.bash:2.luks- backup.md","/nonsense/bash/luks-backup","LUKS Backup",[],"   Backup of LUKS Headers for Encrypted Disks/Volumes   I recently realized that having just the password is not enough to unlock a LUKS volume after a failure or corruption. I learned how to dump the LUKS headers from disks/volumes and to use the serial numbers along with partition names to accurately identify which header corresponds to which disk/partition (I have 10 of them!).  After struggling to do this manually, I asked Qwen3 (an LLM running on my RTX 5090) to create a script that automates the listing and identification of disks, dumps the headers, and stores them in an encrypted archive ready to be backed up on my backup server.  This script:   Lists and identifies disks with their serial numbers  Lists partitions  Dumps headers into a secured folder under   /root  Creates a temporary archive  Prompts for a password  Encrypts the archive with that password  Deletes the unencrypted archive     #!/bin/bash\n   \n   # Directory where LUKS headers will be backed up\n   DEST  =  \"/root/luks-headers-backup\"\n   mkdir   -p   \"  $DEST  \"\n   \n   echo   \"🔍 Searching for LUKS containers on all partitions...\"\n   \n   # Loop through all possible disk partitions (including NVMe and SATA)\n   for   part   in   /dev/sd?   /dev/sd??   /dev/nvme?n?p?  ;   do\n       # Skip if the device doesn't exist\n       if   [   !   -b   \"  $part  \"   ];   then\n           continue\n       fi\n   \n       # Check if the partition is a LUKS encrypted volume\n       if   cryptsetup   isLuks   \"  $part  \"  ;   then\n           # Find the parent disk device (e.g. nvme0n1p4 → nvme0n1)\n           disk  =  $(  lsblk   -no   pkname   \"  $part  \"   |   head   -n   1  )\n           full_disk  =  \"/dev/  $disk  \"\n   \n           # Get the serial number of the parent disk\n           SERIAL  =  $(  udevadm   info   --query=all   --name=  \"  $full_disk  \"   |   grep   ID_SERIAL=   |   cut   -d=   -f2  )\n           if   [   -z   \"  $SERIAL  \"   ];   then\n               SERIAL  =  \"unknown\"\n           fi\n   \n           # Extract the partition name (e.g. nvme0n1p4)\n           PART_NAME  =  $(  basename   \"  $part  \"  )\n   \n           # Build the output filename with partition name and disk serial\n           OUTPUT  =  \"  $DEST  /luks-header-${  PART_NAME  }__${  SERIAL  }.img\"\n   \n           echo   \"🔐 Backing up LUKS header of   $part   (Serial:   $SERIAL  )...\"\n   \n           # Backup the LUKS header to the output file\n           cryptsetup   luksHeaderBackup   \"  $part  \"   --header-backup-file   \"  $OUTPUT  \"\n           if   [[   $?   -eq   0   ]];   then\n               echo   \"✅ Backup successful →   $OUTPUT  \"\n           else\n               echo   \"❌ Backup failed for   $part  \"\n           fi\n       fi\n   done\n   \n   # Create a timestamped compressed tar archive of all header backups\n   ARCHIVE_NAME  =  \"/root/luks-headers-$(  date   +%Y%m%d_%H%M%S).tar.gz\"\n   echo   \"📦 Creating archive   $ARCHIVE_NAME  ...\"\n   tar   -czf   \"  $ARCHIVE_NAME  \"   -C   \"  $DEST  \"   .\n   \n   # Encrypt the archive symmetrically using GPG with AES256 cipher\n   echo   \"🔐 Encrypting the archive with GPG...\"\n   gpg   --symmetric   --cipher-algo   AES256   \"  $ARCHIVE_NAME  \"\n   if   [[   $?   -eq   0   ]];   then\n       echo   \"✅ Encrypted archive created: ${  ARCHIVE_NAME  }.gpg\"\n       # Remove the unencrypted archive for security\n       rm   -f   \"  $ARCHIVE_NAME  \"\n   else\n       echo   \"❌ Encryption failed\"\n   fi\n   Don’t forget to back up   /etc/fstab  and   /etc/crypttab  as well!  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":340,"path":341,"dir":329,"title":342,"description":7,"keywords":343,"body":344},"content:5.nonsense:2.bash:3.socat-proxy.md","/nonsense/bash/socat-proxy","Socat Proxy",[],"   Socat Proxy   This project addresses a common use case:   I have   Beszel , a monitoring container running in host mode, which requires access to the Docker socket to collect container statistics.  To avoid exposing the Docker socket fully to Beszel, I use   Docker Socket Proxy , a container that sits between the Docker socket and the consuming container. It filters requests by setting appropriate permissions, preventing full exposure of the Docker socket.  The problem arises when   Beszel  runs in host mode. In that case, it must connect directly to   Docker Socket Proxy  on a host port, meaning the proxy’s port is exposed. This allows any container or application on the host to access it and use the Docker socket.  This is where   Socat Proxy  comes in. It is a container that:   Creates a UNIX socket  Listens on this socket  Forwards requests to Docker Socket Proxy and back  Replaces the real Docker socket by exposing the proxy socket in the target container via a bind mount (in this case, Beszel)  With this setup, Docker Socket Proxy communicates with Socat Proxy in their isolated bridge network, while the UNIX socket bind-mounted on the host has restricted permissions, preventing access from other containers or applications.  In short:    For example, with Beszel, the configuration would look like this:     services  :\n     socat-proxy  :\n       image  :   git.djeex.fr/djeex/socat-proxy:latest\n       container_name  :   socat-proxy-beszel\n       environment  :\n         -   TARGET_HOST=${TARGET_HOST}\n         -   TARGET_PORT=${TARGET_PORT}\n         -   UNIX_SOCKET_PATH=${UNIX_SOCKET_PATH}\n         -   HOST_SOCKET_PATH=${HOST_SOCKET_PATH}\n         -   UNIX_SOCKET_NAME=${UNIX_SOCKET_NAME}\n       volumes  :\n         -   ${HOST_SOCKET_PATH}:${UNIX_SOCKET_PATH}\n       restart  :   unless-stopped\n       depends_on  :\n         -   ${TARGET_HOST}\n   \n     socket-proxy  :\n       image  :   lscr.io/linuxserver/socket-proxy:latest\n       container_name  :   ${TARGET_HOST}\n       security_opt  :\n         -   no-new-privileges:true\n       environment  :\n         -   CONTAINERS=1\n         -   INFO=1\n       volumes  :\n         -   /var/run/docker.sock:/var/run/docker.sock:ro\n       restart  :   unless-stopped\n       read_only  :   true\n       tmpfs  :\n         -   /run\n   \n     beszel-agent  :\n       image  :   henrygd/beszel-agent:latest\n       container_name  :   beszel-agent\n       restart  :   unless-stopped\n       network_mode  :   host\n       security_opt  :\n         -   no-new-privileges:true\n       volumes  :\n         -   ${HOST_SOCKET_PATH}/${UNIX_SOCKET_NAME}:/var/run/docker.sock:ro\n       environment  :\n         -   #... your Beszel environment variables\n       depends_on  :\n         -   socat-proxy\n  More information is available on the repository:    🐋   Socat Proxy    A lightweight bind-mount socket proxy  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":346,"path":347,"dir":329,"title":348,"description":7,"keywords":349,"body":350},"content:5.nonsense:2.bash:4.hotdisk.md","/nonsense/bash/hotdisk","HotDisk",[],"   HotDisk   When you have a NAS with several drives sitting in a laundry room, temperatures can quickly rise.  \nHard drives are very sensitive to heat and can suffer serious damage if they exceed a certain temperature threshold for too long.  \nAfter a particularly hot summer that caused a few cold sweats while monitoring my drives’ temperatures, I started looking for a way to automatically shut down the server when disk temperatures stay above their safe limit for an extended period.  Since I couldn’t find a convincing solution, I decided to build my own.   The script reads SMART temperature data from all SATA drives every minute.  It counts the number of consecutive minutes the temperature stays above or below the threshold.  It sends Discord notifications if the threshold is exceeded or when the temperature cools down.  It triggers a system shutdown if the temperature stays above the limit for the configured duration.  It logs all temperatures and counter states, and automatically rotates log files.  While I was at it, I also added an installation script that installs the main script, makes it executable, creates a systemd service and timer, and enables them automatically.  \nThe installer also lets you configure various parameters:     Variable  Description  Default Value     MAX_TEMP  Maximum allowed temperature (°C) before the shutdown countdown starts   60    HOT_DURATION  Consecutive minutes above   MAX_TEMP  before shutdown   5    COOL_RESET_DURATION  Consecutive minutes below   MAX_TEMP  to reset all counters   5    LOG_FILE  Path to the main log file   /var/log/hdd_temp_monitor.log    LOG_ROTATE_COUNT  Number of log files to keep   7    LOG_ROTATE_PERIOD  Log rotation period (  daily  or   weekly )   daily    DISCORD_WEBHOOK  Discord webhook URL for notifications   Required  It also runs another script that configures   logrotate  with the parameters defined above.  \nFinally, the installer can even be executed directly via a simple   curl  command followed by one last setup script — perfect for the laziest of us.  I also had to handle several tricky cases: running as root without sudo, using sudo directly, running as a non-sudo user, missing dependencies, permission issues, file creation errors, disk data reading errors, and more.  Concurrent access to the status file also had to be managed carefully.  More details are available directly on the repository:    📜   HotDisk    Keep your drives cool!",{"id":352,"path":353,"dir":329,"title":354,"description":7,"keywords":355,"body":359},"content:5.nonsense:2.bash:5.backrest-docker-stop.md","/nonsense/bash/backrest-docker-stop","Backrest Docker Stop",[356,357,358],"Securely Connecting Backrest and Docker","The Scripts","The Label","   Backrest Docker Stop    Backrest  is a fantastic backup tool. In the case of   Serveex , most of the data that needs to be backed up consists of containers, and those containers often include databases.  The problem? You can’t safely back up a database while it’s running. There are plenty of complex solutions involving database dumps, but often the simplest method is to stop the containers, perform the backup, and then restart them.   Backrest  doesn’t natively provide this functionality, but it does allow the execution of custom scripts triggered by events, for example, at the start and end of a backup plan. Our goal is to stop the containers whose databases need to be backed up when the backup plan starts, and restart them when the backup plan finishes.To achieve this, we’ll need a small Bash script and a secure connection between Backrest and the Docker socket, to enable the following sequence:   The backup plan starts  The event triggers the execution of a custom script  The script contacts Docker and retrieves a list of containers labeled   backrest.backup.stop=true  It stops those containers  The backup plan completes  The event triggers another custom script  The script contacts Docker again, retrieves the same list, and restarts those containers  Securely Connecting Backrest and Docker  To allow   Backrest  to communicate securely with Docker, we’ll use   Docker Socket Proxy .  \nThis avoids exposing the full Docker socket and grants only the necessary permissions.  \nHere’s an example Docker stack:     ---\n   services  :\n     backrest  :\n       image  :   garethgeorge/backrest:latest\n       container_name  :   backrest\n       hostname  :   backrest\n       security_opt  :\n         -   no-new-privileges:true\n       volumes  :\n         -   ...    # your volumes\n       environment  :\n         -   ...    # your environment variables\n         -   DOCKER_HOST=tcp://socket-proxy-backrest:2375\n       restart  :   unless-stopped\n       ports  :\n         -   ...    # your ports\n       depends_on  :\n         -   socket-proxy\n   \n     socket-proxy  :\n       image  :   lscr.io/linuxserver/socket-proxy:latest\n       container_name  :   socket-proxy-backrest\n       security_opt  :\n         -   no-new-privileges:true\n       environment  :\n         -   CONTAINERS=1\n         -   ALLOW_START=1\n         -   ALLOW_STOP=1\n       volumes  :\n         -   /var/run/docker.sock:/var/run/docker.sock:ro\n       restart  :   unless-stopped\n       read_only  :   true\n       tmpfs  :\n         -   /run\n  With this setup, Backrest can communicate with Docker safely and securely.  The Scripts  Below are the scripts to use for   Backrest ’s   start  and   end  backup events.      #!/usr/bin/env bash\n   \n   BACKUP_LABEL  =  \"backrest.backup.stop=true\"\n   BACKUP_CONTAINERS  =  $(  docker   ps   -aqf   \"label=  $BACKUP_LABEL  \"  )\n   for   BC   in   $BACKUP_CONTAINERS\n   do\n       docker   stop   \"  $BC  \"\n   done\n   sleep   10\n     #!/usr/bin/env bash\n   \n   BACKUP_LABEL  =  \"backrest.backup.stop=true\"\n   BACKUP_CONTAINERS  =  $(  docker   ps   -aqf   \"label=  $BACKUP_LABEL  \"  )\n   for   BC   in   $BACKUP_CONTAINERS\n   do\n       docker   start   \"  $BC  \"\n   done\n   sleep   10\n  The Label  Once the scripts are in place and configured for the proper   Backrest  hooks, you just need to add the label   backrest.backup.stop=true  to the   compose.yaml  files of the containers that should stop and restart during backups:     services  :\n     your_service  :\n       ...\n       labels  :\n         -   backrest.backup.stop=true\n  And that’s it!  \nAt the next backup, all containers with the correct label will automatically stop during the backup and restart once it’s finished.  .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":361,"path":362,"dir":363,"title":364,"description":7,"keywords":365,"body":368},"content:Recycled:1.deprecated:1.wireguard-14.md","/recycled/deprecated/wireguard-14","deprecated","Wireguard 14",[34,366,367,137],"Server Side","On the Client Server","   Wireguard   🎯   Goals:   Install Wireguard  Configure clients  Access the secure network  Introduction   Using a VPN allows remote access to a server’s local resources without exposing them to the internet. It’s a clean and secure way to access services like SSH without exposing the port publicly. With a VPN, you can securely connect to your network from anywhere and make devices on different networks communicate.  Here we will use   Wireguard , a secure and high-performance VPN server, using containers:    wg-easy  as the server, providing a very simple web UI to manage connections and download config files (including QR codes for phones)   Wireguard  as the client for Linux systems  Clients are also available for Windows, macOS, iOS, and Android.  The concept:   On the internet, anyone can reach any internet box and thus any exposed server.  Your server is on your local network. It is accessible only locally unless services are explicitly exposed (as we did with Dockge). To access non-exposed resources, you must be on the same local network.  We want to securely access these unexposed services (like SSH) from anywhere.  We also want to connect services between servers, like linking two Dockge instances securely.  To achieve this, we’ll create a   Virtual Private Network  (VPN), i.e., a secure tunnel that only connected machines can use. They’ll appear to be on the same private network.  Additionally, you can add your phone, laptop, or other devices to the VPN and securely access your server resources wherever you are.    In this diagram, machine 1 is part of two networks:   Its local network (devices behind the same router, e.g.   192.168.x.x  – machines 1 and 2)  The VPN network (VPN devices with a second IP, e.g.   10.8.x.x  – machines 1 and 4)  You   can  allow VPN clients to share access to their local networks, but we won’t do that here for security and subnet conflict reasons (e.g., if two remote machines use the same local IP like   192.168.1.1 ).  So only VPN-connected devices can communicate with each other on the VPN, not with other local devices outside the VPN.  Server Side    📋   Checklist:   Ensure port   51820 UDP  is available and properly forwarded through your router to the server (  Source 51820 UDP -> Destination 51820 UDP -> Server ).  Ensure port   51821 TCP  is available for the web UI.      Warning:  This guide uses version   14  of   wg-easy . Version   15  introduces breaking changes incompatible with this configuration.  Folder structure:     root\n   └──   docker\n       └──   wg-easy\n           ├──   config\n           │     └──   etc_wireguard\n           ├──   compose.yaml\n           └──   .env\n  The container runs in   HOST  mode, meaning it uses the host’s network stack directly.  Open Dockge, click   compose , and name the stack   wg_easy .  Paste the following configuration:     ---\n   services  :\n     wg-easy  :\n       network_mode  :   host\n       env_file  :\n         -   .env\n       environment  :\n         -   LANG=en\n         -   WG_HOST=${HOST}\n         -   PASSWORD_HASH=${PW}\n         -   WG_DEFAULT_ADDRESS=${ADDRESS}\n         -   WG_HIDE_KEYS=never\n         -   WG_ALLOWED_IPS=${IPS}\n         -   WG_DEFAULT_DNS=\n         -   UI_TRAFFIC_STATS=true\n         -   UI_CHART_TYPE=1\n       image  :   ghcr.io/wg-easy/wg-easy:14\n       container_name  :   wg-easy\n       volumes  :\n         -   /docker/wg_easy/config/etc_wireguard:/etc/wireguard\n       restart  :   unless-stopped\n       cap_add  :\n         -   NET_ADMIN\n         -   SYS_MODULE\n   ✨   Tip:   You can also specify your own wireguard port with   WG_PORT  Add the Watchtower label to enable automatic updates     services\n     wg-easy  :\n       #...\n       labels  :\n         -   com.centurylinklabs.watchtower.enable=true\n  In   .env :     HOST  =\n   PW  =\n   ADDRESS  =\n   IPS  =\n     Variable  Description  Example     HOST  IP of public access of your host (router ISP's IP if it's at home)   80.75.137.27    PW  Bcrypt password hash,   generate here .   NOTE:  Double the   $  characters   $$2a$$12$$FF6T4QqSP9Ho    ADDRESS  VPN DHCP address range, the   x  must remain, others can vary   10.8.0.x    IPS  IPs routed by clients through the VPN. Use   10.8.0.0/24  to only route VPN traffic. To include local LAN, add   192.168.0.0/16  separated by commas.   10.8.0.0/24  Deploy the stack.  Enable Forwarding on Host  To allow communication between VPN clients, enable:     sudo   sysctl   net.ipv4.ip_forward=  1\n   sudo   sysctl   net.ipv4.conf.all.src_valid_mark=  1\n  Retrieve Configuration Files  To configure clients, download the config files from the server:   Visit   http://your-server-ip:51821  Create a client  Download the config file  Rename it to   wg0.conf     If it fails, check firewall rules.  On the Client Server      Assumes the client is a Linux server with Docker installed  Folder structure:     root\n   └──   docker\n       └──   wireguard\n           └──   config\n           │     └──   wg_confs\n           └──   compose.yaml\n  Create the folder   /docker/wireguard/config/wg_confs :   ✨   Tip:  Use   File Browser  to browse and edit files without terminal     sudo   mkdir   -p   /docker/wireguard/config/wg_confs\n  Copy the   wg0.conf  file downloaded earlier:   ✨   Tip:  Easiest way is to transfer the file via SFTP to   /home/youruser , then move it:     sudo   cp   ~/wg0.conf   /docker/wireguard/config/wg_confs\n  Create   compose.yaml  in   /docker/wireguard :     sudo   vi   /docker/wireguard/compose.yaml\n  Press   i  to enter insert mode and paste:     services  :\n     wireguard  :\n       image  :   lscr.io/linuxserver/wireguard:latest\n       container_name  :   wireguard\n       network_mode  :   host\n       cap_add  :\n         -   NET_ADMIN\n         -   SYS_MODULE   #optional\n       environment  :\n         -   TZ=Europe/Paris\n       volumes  :\n         -   /docker/wireguard/config:/config\n         -   /lib/modules:/lib/modules   #optional\n       restart  :   unless-stopped\n  Press   Esc  then type   :x  to save and exit.  Start the container:     cd   /docker/wireguard\n   sudo   docker   compose   up   -d\n     Repeat for each client  Other Devices     Phone:  Install Wireguard and scan the QR code from the web UI (  http://your-server-ip:51821 )   PC:  Install the Wireguard client and import the config file      Warning:  If a client device is on the same LAN as the server, edit   wg0.conf  and change the endpoint to the local server IP:\n  Endpoint = your-server-ip:51820  And this is the result:    .dark .shiki,html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}.shiki,html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",1785773126445]