SimulationShock
  • Home
  • About
  • Experience
  • Projects
  • bLog
  • Contact

© 2026 Simulation Shock

  • LinkedIn
  • Github

On this page

  • ▸README
  • ▸Art History and Architects of Readymade Computation
  • ▸The Case for Self-Hosting Your Digital Life
  • ▸Dadacenter Architecture
  • ▸Readymade Hardware
    • ▸Hardware categories
    • ▸Essential requirements
    • ▸Where to Look
    • ▸Hardware Acceleration (Optional)
      • ▸Video Transcoding (Reduce CPU load for real-time video transcode)
      • ▸Real-Time AI (Frigate, Object Detection)
      • ▸Local LLM Inference
  • ▸Readymade Operating System
    • ▸Debian-Based Distribution Essentials
      • ▸Kernel
      • ▸Shell
      • ▸SSH
      • ▸Navigation & Productivity
      • ▸Filesystem
      • ▸Text Editors
      • ▸Permissions and ownership
      • ▸Processes
      • ▸Networking
      • ▸Services/Daemons (systemd)
      • ▸Package Management
      • ▸Environment Variables
    • ▸Docker
      • ▸Docker Build
        • ▸Dockerfile/Image
        • ▸Container Registry
      • ▸Docker Run
      • ▸Docker Storage
        • ▸Writable Container Layer
        • ▸Volumes mounts
        • ▸Bind mounts
      • ▸Docker Networks
        • ▸How Containers Find Each Other
          • ▸Subnet Allocation
          • ▸Increase Address Pool to 256 Networks
        • ▸Exposing a Container to the World
          • ▸Publishing Ports
          • ▸Host Network Driver
      • ▸Docker Compose
        • ▸docker-compose.yaml (compose.yml)
        • ▸.env
  • ▸Readymade Logic
    • ▸Application Platforms
    • ▸Deployment Patterns
      • ▸Docker Compose
      • ▸Other Deployment Patterns
        • ▸Single Container
        • ▸Install Scripts
        • ▸Native Packages
    • ▸Networking Glue
      • ▸DNS Rewrites (Finding Services)
        • ▸Router DNS (What Most Homes Use)
        • ▸Dedicated DNS Server (Recommended)
        • ▸Router-Integrated DNS (Advanced)
      • ▸VPN (Remote Access)
        • ▸Self-Hosted VPN (Recommended)
        • ▸Managed VPN (Easiest)
      • ▸Reverse Proxy (Friendly URLs & HTTPS)
        • ▸Single-Service Access (What Most People Start With)
        • ▸Reverse Proxy Gateway (Recommended)
        • ▸Automatic HTTPS (Recommended)
      • ▸Dynamic DNS (Keeping Domains Reachable)
        • ▸Static Public IP (Ideal)
        • ▸Dynamic DNS (What Most Homes Use)
        • ▸Router-Based DDNS (Recommended)
      • ▸Reverse Tunnel (Outbound Only, Reachable by Anyone)
        • ▸Third Party Relay (Easiest)
        • ▸Self-Hosted Relay (Recommended)
  • ▸One Hop Beyond (Networking Detour)
    • ▸DNS, Certs, and the $1 Fix
      • ▸The port problem
      • ▸The HTTPS problem
      • ▸The $1 fix
    • ▸Your Public Endpoint/Address on the Internet
      • ▸Setting up a Stable Hostname with Dynamic DNS (DDNS)
      • ▸Port forwarding
    • ▸Virtual-Private-Networks (VPNs)
      • ▸WireGuard (TOTAL CONTROL, TOTAL PAIN)
        • ▸Requirements
        • ▸Installation
        • ▸Key Generation
        • ▸Server Config
        • ▸Client Config
        • ▸WireGuard, Docker, and IP Forwarding
      • ▸wg-easy (TOTAL CONTROL, LESS PAIN)
        • ▸Installation
        • ▸How Peers Find You
        • ▸Peer Configuration
      • ▸Tailscale (NO CONTROL, NO PAIN)
    • ▸Reverse-Proxies
      • ▸Nginx/Nginx Proxy Manager
        • ▸Installation
        • ▸Creating a Certificate and Adding Proxy Forwarding
    • ▸Reverse Cloud Tunnels
      • ▸Cloudflare Tunnel
      • ▸Tailscale Funnel
      • ▸frp
  • ▸Readymade Models (WIP)
    • ▸Ollama (WIP)
    • ▸Docker Model Runner (WIP)
    • ▸llama.cpp (WIP)
  • ▸Readymade Agents (WIP)
    • ▸Hermes (WIP)
    • ▸OpenCode (WIP)
  • ▸Security (WIP)
  • ▸3-2-1 Backup (WIP)
  • ▸Beneath the Dadacenter (WIP)

On this page

  • ▸README
  • ▸Art History and Architects of Readymade Computation
  • ▸The Case for Self-Hosting Your Digital Life
  • ▸Dadacenter Architecture
  • ▸Readymade Hardware
    • ▸Hardware categories
    • ▸Essential requirements
    • ▸Where to Look
    • ▸Hardware Acceleration (Optional)
      • ▸Video Transcoding (Reduce CPU load for real-time video transcode)
      • ▸Real-Time AI (Frigate, Object Detection)
      • ▸Local LLM Inference
  • ▸Readymade Operating System
    • ▸Debian-Based Distribution Essentials
      • ▸Kernel
      • ▸Shell
      • ▸SSH
      • ▸Navigation & Productivity
      • ▸Filesystem
      • ▸Text Editors
      • ▸Permissions and ownership
      • ▸Processes
      • ▸Networking
      • ▸Services/Daemons (systemd)
      • ▸Package Management
      • ▸Environment Variables
    • ▸Docker
      • ▸Docker Build
        • ▸Dockerfile/Image
        • ▸Container Registry
      • ▸Docker Run
      • ▸Docker Storage
        • ▸Writable Container Layer
        • ▸Volumes mounts
        • ▸Bind mounts
      • ▸Docker Networks
        • ▸How Containers Find Each Other
          • ▸Subnet Allocation
          • ▸Increase Address Pool to 256 Networks
        • ▸Exposing a Container to the World
          • ▸Publishing Ports
          • ▸Host Network Driver
      • ▸Docker Compose
        • ▸docker-compose.yaml (compose.yml)
        • ▸.env
  • ▸Readymade Logic
    • ▸Application Platforms
    • ▸Deployment Patterns
      • ▸Docker Compose
      • ▸Other Deployment Patterns
        • ▸Single Container
        • ▸Install Scripts
        • ▸Native Packages
    • ▸Networking Glue
      • ▸DNS Rewrites (Finding Services)
        • ▸Router DNS (What Most Homes Use)
        • ▸Dedicated DNS Server (Recommended)
        • ▸Router-Integrated DNS (Advanced)
      • ▸VPN (Remote Access)
        • ▸Self-Hosted VPN (Recommended)
        • ▸Managed VPN (Easiest)
      • ▸Reverse Proxy (Friendly URLs & HTTPS)
        • ▸Single-Service Access (What Most People Start With)
        • ▸Reverse Proxy Gateway (Recommended)
        • ▸Automatic HTTPS (Recommended)
      • ▸Dynamic DNS (Keeping Domains Reachable)
        • ▸Static Public IP (Ideal)
        • ▸Dynamic DNS (What Most Homes Use)
        • ▸Router-Based DDNS (Recommended)
      • ▸Reverse Tunnel (Outbound Only, Reachable by Anyone)
        • ▸Third Party Relay (Easiest)
        • ▸Self-Hosted Relay (Recommended)
  • ▸One Hop Beyond (Networking Detour)
    • ▸DNS, Certs, and the $1 Fix
      • ▸The port problem
      • ▸The HTTPS problem
      • ▸The $1 fix
    • ▸Your Public Endpoint/Address on the Internet
      • ▸Setting up a Stable Hostname with Dynamic DNS (DDNS)
      • ▸Port forwarding
    • ▸Virtual-Private-Networks (VPNs)
      • ▸WireGuard (TOTAL CONTROL, TOTAL PAIN)
        • ▸Requirements
        • ▸Installation
        • ▸Key Generation
        • ▸Server Config
        • ▸Client Config
        • ▸WireGuard, Docker, and IP Forwarding
      • ▸wg-easy (TOTAL CONTROL, LESS PAIN)
        • ▸Installation
        • ▸How Peers Find You
        • ▸Peer Configuration
      • ▸Tailscale (NO CONTROL, NO PAIN)
    • ▸Reverse-Proxies
      • ▸Nginx/Nginx Proxy Manager
        • ▸Installation
        • ▸Creating a Certificate and Adding Proxy Forwarding
    • ▸Reverse Cloud Tunnels
      • ▸Cloudflare Tunnel
      • ▸Tailscale Funnel
      • ▸frp
  • ▸Readymade Models (WIP)
    • ▸Ollama (WIP)
    • ▸Docker Model Runner (WIP)
    • ▸llama.cpp (WIP)
  • ▸Readymade Agents (WIP)
    • ▸Hermes (WIP)
    • ▸OpenCode (WIP)
  • ▸Security (WIP)
  • ▸3-2-1 Backup (WIP)
  • ▸Beneath the Dadacenter (WIP)
Cover image for THERE IS NO CLOUD BUT YOUR OWN
dockernetworkingd.i.y

THERE IS NO CLOUD BUT YOUR OWN

A Technical D.I.Y Primer for Self-Hosting Readymade Logic in a World of Algorithms and Agents

May 1, 2026·Updated June 23, 2026
Loading...

README

While Networking Principles for DIY and Interactive Systems explores the infrastructure that our devices use to communicate, THERE IS NO CLOUD BUT YOUR OWN goes a step further and demonstrates that the open-source community has contributed a great deal of software that you can deploy today yourself.

This gesture now extends to intelligent systems by also deploying your own autonomous agents to collaborate with. We later experiment by encouraging these systems to lean into the absurd, unpredictable, and accidental.

By the end you have a Dadacenter and so does someone else and so does someone else and so does someone else and so does someone else...

Art History and Architects of Readymade Computation

Man Ray - The Gift (1921)

Man Ray – The Gift (1921)

Readymade Logic

Lenovo - ThinkCentre Tiny Desktop Computer (2017)

Marcel Duchamp - Fountain (1917)

Marcel Duchamp – Fountain (1917)

Futurism was an art movement that embraced acceleration, technology, speed, and disruption. Where Futurism celebrated the modern world, Dada was born out of disgust viewing the same logic, rationality, progress, and faith that Futurism admired as having collapsed into catastrophe. It emerged from the wreckage using irony, absurdity, and a sort of anti-art that redefined artistic value from a focus on technical craftsmanship to one centered on concept, meaning, and social commentary. Readymades are a concept introduced by Dada artist Marcel Duchamp, in which ordinary, mass-produced objects are presented as art with little or no alteration. The skill was no longer in the making but in the selecting, the placing, and the idea. A urinal becomes a fountain, an iron studded with tacks becomes a gift, and a mass-produced ThinkCentre pulled from corporate surplus becomes a substrate. Before mass-produced computation could sit on a shelf waiting to be repurposed, the computer itself had to be imagined, formalized, and built.


Alan Turing

Alan Turing

Alan Turing exposed weaknesses in secure communication systems as a cryptographer working to break the German Enigma cipher during World War II. This strengthened the design of modern cryptographic protocols that enable online banking, e-commerce, and private messaging. Beyond cryptography, his conceptual invention of the Turing machine represented the concept of a modern computer in an ideal form and used it to formally establish the capabilities and limitations of computation itself. The "Learning Machines" in his famous 1950 paper "Computing Machinery and Intelligence", Turing envisioned a radically different approach for Artificial Intelligence by suggesting that instead of attempting to program adult intelligence from the top down, it would be better to build "child-like" machines that arrive knowing nothing and are educated into capability through experience, reward, and a carefully chosen curriculum. This process has been extremly useful in solving problems that were too complex to program explicitly. The 2020s AI Revolution has made available a variety of Large Language Models (LLMs) that are "proprietary" (GPT-4, Gemini, Claude, Grok) and open-weight (Llama-3, Mistral, Gemma, DeepSeek).

Claude Shannon established the field of Information Theory with his seminal 1948 paper "A Mathematical Theory of Communication". In it, the bit is established as the fundamental unit of information and is used to measure the resolution of uncertainty in storing, sending, correcting, and compressing information across channels like telephone lines, fiber optic cables, and the radio spectrum. The decades that followed would revolutionize how the entire world communicates. Although Shannon is celebrated as the mathematician who gave the digital age its foundations, he can also be seen as a Dadaist in several interesting ways. After sparking the rise of the information age, he fucked right off and began entertaining his curiosity by creating all sorts of toys and gadgets that would have delighted Marcel Duchamp and baffled his colleagues at Bell Labs. He pursued absurdities like Juggling Machines and formalized the mathematical relationship between throw time, dwell time, number of balls and hands. A Motorized Pogo Stick and increasingly unhinged variations of the unicycle. But then there was the Flame-Throwing Trumpet, the Rocket-Powered Frisbee, and the Ultimate Machine which was a wooden box with a single switch that, when flipped, extended a mechanical hand, turned itself off, and retreated back inside.

Claude Shannon

Claude Shannon

John Von Neumann

John Von Neumann

John von Neumann accelerated the spread of the stored-program architecture in 1945 by publishing "First Draft of a report on the EDVAC¹" written while consulting for Eckert and Mauchly the inventors of ENIAC². ENIAC was commissioned by the U.S Army to calculate artillery firing tables. The successor, EDVAC enhanced ENIAC's design by introducing binary arithmetic and making the machine reprogrammable in software rather than physically altering the hardware circuits. The vast majority of computers share this architecture today whether it's a laptop, phone, server, microcontroller, or game console because they share the following components:

  1. A central arithmetic unit to perform arithmetic operations
  2. A central control unit to sequence operations performed by the machine
  3. Memory that stores data and instructions
  4. An "outside recording medium" to store input to and output from the machine
  5. Input and output mechanisms to transfer data between the memory and the outside recording medium

Electronic Discrete Variable Automatic Computer¹ Electronic Numerical Integrator and Computer²

Norbert Wiener was interested in modeling the similarities between how living organisms and machines regulate themselves by continuously sensing their current state, comparing it to a desired state, and using the difference to correct their own behavior. This observation became the transdisciplinary field known as Cybernetics through his 1948 publication "Cybernetics: Or Control and Communication in the Animal and the Machine". Cybernetics went on to influence not only computer science, robotics, and artificial intelligence but also psychology, anthropology, sociology, philosophy, and art. The name can be seen as a universal logic of self-correction and interconnectedness, and has an interesting lineage. It traces back to ancient Greek as κυβερνήτης (koo-bur-net-ees), a helmsman that keeps a ship on course by continuously reading drift and correcting for it. It was extended by Plato as metaphor for governance, and was repurposed again into the french vocabulary as cybernétique.

Norbert Wiener

Norbert Wiener

The Case for Self-Hosting Your Digital Life

In the early days of the web, the Apache HTTP Server dominated as the most widely deployed HTTP server on the internet because it was open-source, freely available, and ran reliably across multiple operating systems. Rather than spending resources to engineer and develop similar proprietary web server software, developers could deploy Apache immediately. Over the course of the late 1990s and early 2000s, the number of websites expanded from only a few thousand to tens of millions, as internet adoption accelerated globally. This rapid growth enabled the creation of countless niche communities through discussion forums, personal blogs, and early e-commerce platforms.

The rise of Platforms like Facebook, Youtube, Instagram, Discord, and Reddit have somewhat centralized the internet by funneling vast amounts of user-generated content, discussion, and social interaction through a small number of corporate-controlled platforms, shaping trends, visibility, and the way people communicate online. Artificial intelligence is creeping into every facet of this digital ecosystem and beyond, from content recommendation algorithms and automated moderation to personalized advertisements and autonomous systems poking and nudging at your attention, preferences, and behavior. Ownership of hardware, software, and data is being increasingly funneled into subscription-based and cloud-controlled ecosystems, where users rely on proprietary platforms for access, storage, and services rather than owning or controlling their digital tools and content.

With the explosion of content and now AI-generated content, someday you may no longer be able to find that one song, video, website, or artist that you remember.

  • Videos get removed or demonetized
  • Websites shut down
  • databases get corrupted
  • songs disappear from streaming platforms
  • algorithms bury what once felt unforgettable

Dadacenter Architecture

The Readymade Stack

The Dadacenter is an assemblage of hardware you did not build, software you did not write, and AI you did not train. Provisioning your own anti-cloud is a far cry from slapping a sticker on a urinal, but the architectural pattern that once required an institution to deploy fits inside a shoebox on a shelf. The newest gesture in this assemblage is Agents driving Models to influence the Logic, and Operating System. Beneath them all, Hardware as a substrate. They observe their own state, correct their own failures, and pursue goals you set and steer in plain English.

The problem with a Readymade Stack is you don't know how anything works the higher up you go without peeling back each layer. When things aren't working as expected you don't know where to start troubleshooting. So this primer starts at the bottom and assembles a traditional private cloud that you can securely access wherever you are. Then we choose: stop here and have a homelab, or add Models and Agents and have a Dadacenter. Its purpose is to find out what happens when you run autonomous agentic systems on infrastructure you own to augment your environment as a practice.

Readymade Hardware

Use whatever you have to get started, whether it's an old laptop or salvaged workstation. This is okay when learning and not yet committed because your found machine will likely consume more energy and therefore cost more over time. Newer Mini PCs with a modern energy-efficient processor are more rare and costly upfront, but will save you money in the long run. If the machine runs 24/7 then the difference between 15W and 100W depending on local electricity rate may roughly cost $50/year or $300/year. Equipping it with a GPU unlocks more capabilities like Real-Time Object Detection, Multiple High-Resolution Cameras, and Advanced Video Analytics, but automatically increases energy consumption. Specialized accelerators like the Coral Edge TPU share similar cost-saving benefits. Most of the effort is the initial time investment to learn and set everything up yourself. After that, maintenance becomes “very minimal”.

It's currently pretty expensive to upgrade your assembly to support fully independent Models and Agents. This will likely become more accessible in the near future as models improve, and more specialized hardware accelerators reach consumers. Those new accelerators are kind of like the next generation of gaming hardware. A few years from now you'll either own one the same way you own a console, or tap into them remotely the way Cloud Gaming works today.

Hardware categories

Budgetmaxxing

Raspberry Pi / ARM SBC
5W · entry point for beginners
Good for infrastructure services (DNS, VPN, home automation, light self-hosted apps). Not viable for much else.
Old laptop
30–60W · abundant
This is how i started, but the battery makes it a concern so get rid of it if possible.

Balancemaxxing

Mini PC / SFF (primary recommendation)
15–35W · NUC, ThinkCentre Tiny, EliteDesk Mini, OptiPlex Micro
Mass-produced corporate surplus built for the cubicle, salvaged for the shoebox.
Apple Silicon Mac mini
10–30W · M1/M2, silent
Unified memory punches above weight for low-power local AI.

Siliconmaxxing

Recycled workstation
65–100W · Dell OptiPlex tower, HP Z-series
Expandable, more storage bays. Good for NAS or compute-heavy workloads.
GPU workstation
200–350W under load · Precision or Z-series + used 3060 12GB / 3090
Unlocks running models locally at useful scale.

Essential requirements

ComponentMinimumRecommendedNotes
CPU4+ cores, x86_64 (Intel/AMD)8+ cores, x86_64No ARM; 6th gen Intel+ preferred
RAM8GB16GB32GB if multi-agent orchestration
Primary Storage256GB SSD512GB SSDNVMe preferred for OS/Docker
Secondary Storage2TB HDD8TB HDDMedia storage; optional if CPU-only
NetworkFast Ethernet (100 Mbps)Gigabit Ethernet (1 Gbps)Multi-Gigabit Ethernet (2.5 Gbps) for
USB2x USB 3.02x USB 3.0Smart-home receivers, storage, etc.
PSU500W 80+ Bronze850W 80+ Gold1000W if RTX 3090
RouterConsumer WiFi router with OpenWrt/MerlinTiny mini-PC running OPNsenseOptional; bridge WiFi AP to router

Where to Look

  • Family/Friends - free or cheap hand-me-downs
  • E-Waste recycling centers - IT departments offloading hardware
  • Dumpster diving / curb finds - near corporate offices
  • Thrift stores - Goodwill, Salvation Army
  • Facebook Marketplace - similar to Craigslist
  • OfferUp - similar to Craigslist
  • Public-sector surplus — retired equipment from government, schools, and other publicly funded institutions
  • eBay - largest selection, established sellers
  • Woot - deep discounts on electronics
  • Newegg refurbished
  • Amazon Renewed

Hardware Acceleration (Optional)

Video Transcoding (Reduce CPU load for real-time video transcode)

  • GPU - GeForce RTX 2060 minimum, GeForce RTX 3060 recommended
  • Intel Quick Sync - Free hardware encoder built into most intel cpus

Real-Time AI (Frigate, Object Detection)

  • Coral Edge TPU — Most efficient for real-time camera detection; ~5W power, no GPU needed
  • Discrete GPU — Overkill unless running lots of camera streams

Local LLM Inference

  • GPU - minimum GeForce RTX 3060 (12GB), recommended GeForce RTX 3090 (24GB)
  • NPU - Emerging options, not yet widely spread

Readymade Operating System

There is no better example than the Linux Kernel and the many operating systems built around it. Forked into thousands of variants for every conceivable use. It is the non-physical canvas that all readymade logic uses to bootstrap an entire ecosystem of software and community-driven mutations. There are a few major flavors to choose from, but Debian will be our chosen operator. We will cover what commands and mechanisms are necessary to navigate, configure, and run the system. But running lots of apps directly on the operating system causes problems. Every application brings its own dependencies and assumptions about the machine it's running on, and eventually they contradict.

A key technology in simplifying self-hosting and making it more accessible are docker containers, which package applications and all their dependencies into portable, reproducible units that run the same way on any machine. Think of a container as a little computer that shares the host's kernel instead of booting its own, packing just the app and its dependencies into a sealed environment.

Building on this, entire platforms exist that assemble containers for you. If you are just starting out, platforms like CasaOS, UmbrelOS, and Cosmos accumulate these layers into a turnkey solution that orchestrates containers behind a dashboard that feels like an app store rather than a terminal. Beginners can start tinkering immediately and experienced hobbyists can quickly spin up entire Readymade Infrastructure specialized for families, small businesses, communities, and creative practices.

Debian-Based Distribution Essentials

Kernel

The main job of any operating system is abstracting the users and developers away from the nitty gritty. The Kernel acts as an intermediary between software applications and the computer’s hardware. It's the part you don't see but makes all the magic happen, and let's just leave it at that.

Shell

The shell is how you talk to your computer by typing instead of clicking. You type a command, hit enter, the computer does it. Bash (Bourne Again SHell) is the most common shell on Linux. It works the same way on a machine across the room or across the internet.

SSH

ssh username@hostname/ip - Secure Shell opens a shell on another machine

Navigation & Productivity

Tab — autocomplete; press twice for options
↑ / ↓ — scroll through command history
Ctrl-R — search command history
history — list recent commands
clear or Ctrl-L — clear the screen

Filesystem

pwd - shows current location in the filesystem
cd - move between locations in the filesystem
ls - list files
cp — copy; cp -r for directories
mv — move or rename
rm — delete; rm -rf deletes directories recursively
mkdir — make a directory; mkdir -p creates parent dirs as needed
touch — create an empty file or update its timestamp
cat — print a file to the terminal (short files)
grep — search inside files; grep -r "thing" . searches everything in the current directory
df -h — disk space, human-readable

Text Editors

nano — keyboard-driven text editor
vim or vi — keyboard-driven text editor

Permissions and ownership

chmod — change permissions; chmod +x file makes a file executable
chown — change owner; chown user:group file
sudo — run a command as root (the one that makes things possible and breaks things irrevocably)

Processes

ps aux — list all running processes
top or htop — live process view (Task Manager)
kill — stop a process by PID
Ctrl-C — stop the current command

Networking

ping — is this host reachable?
curl — fetch a URL; curl -O saves it
wget — also fetches URLs, slightly different defaults
ss -tlnp — what's listening on which port
ip a — show network interfaces

Services/Daemons (systemd)

systemctl status <service> — is it running?
systemctl start / stop / restart / enable / disable
journalctl -u <service> -f — live logs for a service

Package Management

apt update — refresh the package list
apt upgrade — install available updates
apt install <name> — install something
apt remove <name> — uninstall

Environment Variables

echo $VAR — print an environment variable
export VAR=value — set one for this session
which and whereis — find where a binary lives
~/.bashrc — the file you edit to make changes stick across sessions\

Docker

Bare-metal, VMs, Containers

In the early internet the only option was to obtain a physical server, install an OS, and run your applications directly on bare-metal hardware. Most machines sat idle and were unable to safely share their resources across multiple apps without risking one crashing or compromising the others. The concept of carving one physical server into many virtual machines became known as hardware virtualization. This is done by a software layer known as a hypervisor that sits between the physical hardware and the guest operating systems, allowing each virtual machine to run as if it has the hardware to itself. This became the backbone of the cloud until developers realized they were shipping an entire operating system just to run a single application. Eventually, Docker became a lighter more convenient way to deploy apps and services to any environment by leveraging built-in Linux kernel features to isolate applications.

Docker Build

Dockerfile/Image

A Dockerfile answers "How do I create this environment?", and defines the base operating system, dependencies to install, files to copy, commands to run, and what boots first. Docker can then build a portable image or snapshot of that environment to store locally and push to a container registry for distribution and reuse. An instance of this image is known as a container, with its own filesystem, network interface, and isolated environment. Many open-source projects already maintain a Dockerfile and built image that is ready to use.

Container Registry

A Container Registry acts as a centralized library for readymade logic ready to deploy. They can be public or private and you can even run your own. They provide mechanisms for anyone to Pull/Push Images, Maintain versions, and Authenticate access.

The registries you'll actually encounter:

  • docker.io — Docker Hub, the default. Most open-source projects publish here.
  • ghcr.io — GitHub Container Registry. Newer projects increasingly publish alongside their source code.
  • lscr.io — Linuxserver.io. Homelab-optimized images.
  • nvcr.io — NVIDIA NGC. CUDA base images and GPU-accelerated runtimes.

Every image reference is shorthand for a full address:

  • archivebox/archivebox -> docker.io/archivebox/archivebox:latest

A fully qualified reference leaves nothing to assumption:

  • ghcr.io/immich-app/immich-server:v1.106.4 --> registry/namespace/imagename:version

Docker Run

A single container can be started directly with docker run, which pulls the image if it's not already on disk, creates a container from it, and starts it in one step:

bash
docker run -v $PWD:/data -it archivebox/archivebox init

Lists running containers:

bash
docker ps

Lists every container, including stopped ones:

bash
docker ps -a

Stops a running container without deleting it:

bash
docker stop <container name>

Starts a stopped container back up:

bash
docker start <container name>

Deletes a stopped container permanently:

bash
docker rm <container name>

Opens a command inside a running container:

bash
docker exec -it <container name> <command> <args0> <args1>

Follows the live logs of a container:

bash
docker logs -f <container name>

Docker Storage

Containers are short-lived by design. That is a feature, not a limitation. The same container image can be used to create many container instances across multiple computers, data centers, or geographic regions. In a Readymade App, the application logic can be separate from the data it operates on, and often is. Different apps may rely on data persistence while others may not. For example, if you use Immich to upload photos/video from your cell phone to your homelab, you want to be able to copy that data elsewhere for backups. The container can be replaced, but the uploaded media should live in a persistent volume or bind mount so it survives.

Writable Container Layer
container lifecycle

Each new container will maintain a temporary storage layer. That layer survives docker stop and docker start — the container still exists, it's just not running. The moment you run docker rm, the container and its writable layer are gone permanently. The image it came from is untouched and can spin up a new container at any time. Anything that needs to outlive the container goes in a volume or bind mount

Run without --rm — container persists after exit, writable layer intact:

bash
docker run --name mycontainer nginx

Stop it — container still exists, writable layer intact:

bash
docker stop mycontainer
docker start mycontainer   # comes back, data still there

Remove it — writable layer gone:

bash
docker rm mycontainer

Run with --rm — container removes itself on exit, writable layer gone:

bash
docker run --rm --name mycontainer nginx
Volumes mounts

Docker can create separate storage volumes that exist outside the lifecycle of a container. They can be automatically or intentionally created and will remain until explicitly deleted with docker volume rm or docker volume prune regardless of how many times the container using them is created, stopped, or destroyed. Volumes are preferred for sharing data between containers. Multiple containers can mount the same volume at the same time. The contents of the volume exist on disk at /var/lib/docker/volumes/ but are tucked away and managed by docker. Use bind mounts instead if you need direct access to the files from the host. Some Readymade Apps prefer using a Docker Volume and will automatically create one when they are started for the first time.

Let Docker create the volume automatically at run time:

bash
docker run -v mydata:/app/data nginx 

Create a volume explicitly then use it:

bash
docker volume create mydata
docker run -v mydata:/app/data nginx

Volumes can be mounted as Read-Only:

bash
docker run -v mydata:/app/data:ro nginx
yaml
volumes:
  - mydata:/app/data:ro

Inspecting and managing volumes:

bash
docker volume ls                    # list all volumes
docker volume inspect mydata        # see where it lives on disk
docker volume rm mydata             # delete a specific volume

removes any volume that is not currently mounted to at least one container — running or stopped:

bash
docker volume prune                  # delete all unused volumes
Bind mounts

Bind mounts are preferred when you need to share data between a container and the host. Most Readymade Apps ship with a config file that controls their behavior like Home Assistant has configuration.yaml, and Frigate has config.yml. Some apps need an entire directory bind mounted rather than one config file because they write multiple files into it, or because the directory is the workspace the app operates on. Immich requires an data/ directory for incoming photos/videos. Frigate requires a media/ directory for recordings and clips.

container lifecycle

Bind Mount Syntax [host path] : [container path]:

bash
-v /host/path:/container/path

Mounting a host directory into a container:

bash
docker run -v /srv/appdata:/app/data nginx

Mounting a single configuration file:

bash
docker run -v /srv/homeassistant/configuration.yaml:/config/configuration.yaml home-assistant

Docker Networks

How Containers Find Each Other

The Default Bridge is the network Docker automatically creates. Every container ran without specifying a --network will attach itself to this network and get assigned an ip address from default pool 172.17.0.0/16. It's considered legacy and in practice user-defined networks are created automatically which allow you to use container names as a substitute for the ip addresses. Container names are stable while IPs are not. Containers on the default bridge inherit the host's DNS settings and can only reach each other by IP. Containers on a user-defined network get Docker's embedded DNS server (127.0.0.11), which resolves container names automatically if they are on the same network. A container on Network A cannot resolve a container on Network B by name. A container attached to multiple networks can resolve names across all of them.

Docker Networks DNS
Subnet Allocation

When you create a user-defined network whether automatically through docker-compose.yaml or docker network create that network and it's containers are assigned ip addresses from an internal predefined "default address pools" that can be overwritten in /etc/docker/daemon.json

By default Docker gives you room for 31 networks total. Every docker compose up consumes one. On a dense homelab you will hit the ceiling before you realize it. No warning, just a failed docker network create when you try to stand up your 31st "Readymade App".

The built-in default will use:

json
{
  "default-address-pools": [
    { "base": "172.17.0.0/16", "size": 16 }, // 1 Network/65,534 hosts each
    { "base": "172.18.0.0/16", "size": 16 }, // 1 Network/65,534 hosts each
    { "base": "172.19.0.0/16", "size": 16 }, // 1 Network/65,534 hosts each
    { "base": "172.20.0.0/14", "size": 16 }, // 4 Networks/65,534 hosts each
    { "base": "172.24.0.0/14", "size": 16 }, // 4 networks/65,534 hosts each
    { "base": "172.28.0.0/14", "size": 16 }, // 4 networks/65,534 hosts each
    { "base": "192.168.0.0/16", "size": 20 } // 16 networks/4,094 hosts each
  ]
}

Every network is burning thousands of host addresses while most apps/services only really need 3-6.

Increase Address Pool to 256 Networks

In a long-term homelab, you might eventually exceed Docker’s default pool of roughly 31 bridge networks. But you are very unlikely to need more than 254 user-defined networks, and even less likely to need 254 container IPs inside each one.

Check your current daemon.json first:

bash
cat /etc/docker/daemon.json

If the file is empty or doesn't exist, write the config directly:

bash
echo '{"default-address-pools":[{"base":"10.200.0.0/16","size":24}]}' | sudo tee /etc/docker/daemon.json

Then restart Docker:

bash
sudo systemctl restart docker
Exposing a Container to the World

By default a container's ports are only reachable from the Docker host and other containers on the same network (bridge, user-defined). No other device on your LAN or across the internet can reach it until you expose it. Two ways to expose a container to the outside world are by publishing a port or in some cases having the container share the host's network.

Docker Networks DNS
Publishing Ports

-p 8080:80 is shorthand for --publish and specifies the port number on your host machine where you want to receive traffic, and the port number within the container that's listening for connections.

Host Network Driver

using docker run with --network host or docker compose with network_mode: host will make the container share the host's network. This means if any processes in the container are listening on any ports then those processes are available at the Host IP Address.

The docker-compose.yaml for Home Assistant uses network_mode: host to discover devices via mDNS and broadcast protocols that aren't made to announce presence across separate networks.

Docker Compose

More fully-featured self-hosted projects will typically require communication across multiple containers such that one container may run the application server (e.g. a web app written in Node.js, Django, or Flask), another container may run a database (e.g. PostgreSQL, MySQL), and a third container may run a cache or message broker (e.g., Redis, RabbitMQ). A docker-compose.yaml file makes it easy to modify the configuration of each container and simplifies the processes of defining and running multi-container Docker apps.

I think it's easier to stay organized by explicitly converting a docker run command to a docker-compose.yaml file instead. Not every projects will provide a docker-compose.yaml and .env.example but will give a docker run command only

docker-compose.yaml (compose.yml)

A YAML is a type of text file structured in a certain way that serves as a clean, human-readable format to define settings, automations, and infrastructure on your computer, on your server, or on your cloud.

Docker Compose commands read and operate on the docker-compose.yaml (deprecated) or compose.yaml found in the current working directory.

code
/opt/
├── immich/
│   ├── compose.yaml          ← docker compose up -d  (runs Immich stack)
│   ├── .env
│   └── library/
│
├── frigate/
│   ├── docker-compose.yaml   ← docker compose up -d  (runs Frigate stack)
│   ├── .env
│   └── config/
│
└── wg-easy/
    ├── compose.yaml          ← docker compose up -d  (runs wg-easy stack)
    ├── .env
    └── config/

Following logs on a single service:

bash
docker compose logs -f service_name

Status overview scoped to just that project's containers:

bash
docker compose ps

The Standard update cycle:

bash
docker compose pull
docker compose up -d

Builds/pulls images if needed and starts every service in the stack as a background process:

bash
docker compose up -d

Stops and removes the containers and networks from up, leaving volumes and data intact.

bash
docker compose down

Stops the running containers without removing them, leaving them in hiatus:

bash
docker compose stop

Resumes the stopped containers:

bash
docker compose start
.env

When you run docker compose up, Compose automatically reads a .env file in the same directory as the docker-compose.yaml and substitutes any variables it finds into the docker-compose.yaml. This allows you to modify secrets and environment-specific values without hardcoding them.

Example .env for Immich

text
UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
IMMICH_VERSION=release
DB_PASSWORD=mysecret
DB_USERNAME=postgres

simplified docker-compose.yaml for Immich

yaml
services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    volumes:
      - /srv/media-storage/immich:/data
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - '2283:2283'
    depends_on:
      - database
    restart: always
    healthcheck:
      disable: false
 
  database:
    container_name: immich_postgres
    image: ghcr.io/immich-app/postgres
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    shm_size: 128mb
    restart: always
 

Readymade Logic

Application Platforms

The Apache HTTP Server was a ready-made, free software platform that allowed anyone with an internet connection to host and serve content to users across the world. After decades of open-source development, we now have a vast ecosystem of software or "readymade logic" that goes above and beyond from just serving web pages to complete production-ready systems that replicate major cloud platforms akin to Google Suite (Docs, Photos, Drive, Sheets, etc), Dropbox, ChatGPT, Notion, Discord, and much more. Visit https://awesome-selfhosted.net/ for an idea of what's possible.

A few ready-made free software platforms you can find are:

  1. Immich is a self-hosted photo and video backup solution with a focus on privacy and simplicity.
  2. Home Assistant is an open-source platform for smart home automation that focuses on privacy and local control.
  3. Nextcloud is a private cloud solution, similar to Google Workspace, providing Nextcloud Office (Writer, Sheets, etc.) for document editing and collaboration. It offers file storage, synchronization, and sharing, allowing teams to access and manage their data securely.
  4. Open-WebUI is an AI platform designed to operate entirely offline and supports various LLMs like Ollama and OpenAI-compatible APIs.
  5. VaultWarden is a lightweight, open-source implementation of Bitwarden, a password manager.
  6. Frigate is a real-time object detection NVR (Network Video Recorder) that focuses on smart video surveillance with deep learning and edge AI.
  7. Trilium is a hierarchical note-taking application with a rich set of features, including support for rich text, markdown, and media embedding.
  8. Matrix is an open-source, decentralized communication protocol that powers secure, real-time chat and VoIP apps.
  9. Paperless-ngx is an open-source document management system that transforms your physical documents into a searchable online archive.

Deployment Patterns

Docker Compose

Examples: Immich, Nextcloud, Trilium

Immich is a self-hosted photo/video cloud platform that allows family and friends to connect and use your infrastructure as cloud storage. It has features like Facial recognition, person clustering, memories, semantic search, mobile backup, and family-friendly multi-user sharing. All this and more, but how do you install this and get it running?

Like most Readymade Logic available, the Docker Compose is commonly provided along with specific instructions.

  • Step 1 - Obtain the Application
    • Download the project's files, which typically include a docker-compose.yaml, .env.example, configuration files, and documentation.
  • Step 2 - Configure the Application
    • Populate the .env file and modify any configuration files to match your environment, passwords, domain names, storage locations, and other preferences.
  • Step 3 - Deploy the Application
    • Start the containers using docker compose up -d, allowing Docker to download the required images and create the application stack.
  • Step 4 - Access the Application
    • Open the application's web interface, complete any first-time setup, and verify that it is functioning correctly.
  • Step 5 - Maintain the Application
    • Periodically update containers, back up important data, and review logs when troubleshooting problems.

Other Deployment Patterns

Single Container

Examples: mosquitto-broker, uptime-kuma, AdGuard Home

Smaller projects may only require a single Docker image and can be started with a single command.

docker run -it -p 1883:1883 -v "$PWD/mosquitto/config:/mosquitto/config" eclipse-mosquitto

Install Scripts

Examples: Tailscale, CasaOS, UmbrelOS

Very commonly given are one-liner bash scripts that perform heavy lifting:

  • A Native Install, the script installs the application directly onto the OS.

    bash
    curl -fsSL https://tailscale.com/install.sh | sh
  • A Docker Bootstrap, the script installs Docker if missing, then deploys the actual application as a container or compose stack on your behalf.

    bash
    curl -fsSL https://get.casaos.io | sudo bash
Native Packages

Examples: WireGuard, openssh-server

Some software is installed directly on the operating system using the package manager.

sudo apt install wireguard -y

Networking Glue

As your Dadacenter grows, a few supporting services become increasingly useful for making your applications easier to find, access, and secure both inside and outside your local network. These are first introduced but partly demonstrated in the One Hop Beyond (Networking Detour) section as needed.

DNS Rewrites (Finding Services)

Examples: AdGuard Home, Pi-hole

DNS (Domain Name System) translates human-friendly names into IP addresses. DNS rewrites allow you to create your own names for devices and services on the local network. For example, immich.home.arpa can automatically resolve to 192.168.50.10, making services easier to find and remember. AdGuard Home and Pi-hole act as DNS forwarders. They answer local records such as immich.home.arpa, block unwanted domains, and forward everything else to an upstream DNS provider such as Cloudflare, Google, Quad9, or your ISP.

Router DNS (What Most Homes Use)

By default, most routers provide DNS services to devices on the network and forward requests to an ISP or public DNS provider.

Dedicated DNS Server (Recommended)

A common Dadacenter setup is running AdGuard Home or Pi-hole on a dedicated machine and configuring the router to advertise it as the network DNS server.

Router-Integrated DNS (Advanced)

Some routers, firewalls, and network appliances can run AdGuard Home, Pi-hole, or similar DNS software directly on the network edge.

VPN (Remote Access)

Examples: WireGuard, Tailscale

A Virtual Private Network (VPN) securely extends your private network across the internet. It allows you to access your Dadacenter from a phone, laptop, or tablet even when you are away from home.

Self-Hosted VPN (Recommended)

A VPN server runs inside your Dadacenter and devices connect directly to it over the internet.

Managed VPN (Easiest)

A third-party service coordinates device discovery and connectivity for you.

Reverse Proxy (Friendly URLs & HTTPS)

Examples: Nginx Proxy Manager (NPM), Traefik, Caddy

A reverse proxy acts as a front door for your web services. Instead of remembering ports like :2283, :8123, or :5000, it routes incoming requests to the correct application like immich.home.arpa, frigate.home.arpa, and so on. It's also the go to method for automatically managing HTTPS certificates and safely exposing multiple web services through a single connection to your router.

Single-Service Access (What Most People Start With)

Applications are accessed directly through their IP address and port number.

Reverse Proxy Gateway (Recommended)

A reverse proxy receives incoming web traffic and routes requests to the correct service.

Automatic HTTPS (Recommended)

Modern reverse proxies can automatically obtain and renew TLS certificates, allowing applications to be securely accessed over HTTPS.

Dynamic DNS (Keeping Domains Reachable)

Examples: Cloudflare DDNS, Router-Based DDNS, DDClient, DuckDNS

Most residential internet connections receive a public IP address that can change over time. Dynamic DNS updates your DNS records automatically so your domain name continues pointing at your Dadacenter even when that address changes.

Static Public IP (Ideal)

Some internet connections provide a permanent public IP address that never changes.

Dynamic DNS (What Most Homes Use)

A service periodically updates DNS records whenever your public IP address changes.

Router-Based DDNS (Recommended)

Many modern routers and firewalls can automatically update DNS records without requiring additional software.

Reverse Tunnel (Outbound Only, Reachable by Anyone)

Examples: Cloudflare Tunnel, Tailscale Funnel, frp

The Dadacenter may be operating behind restricted ISP's firewalls that block incoming traffic. You may not have access to the router to forward ports. Your ISP may not have assigned you a real public IP at all, instead placing you behind Carrier-Grade NAT (CGNAT) sharing one address across hundreds of households. A reverse tunnel takes advantage of nearly every firewall's leniency for clients within the network communicating outside to websites and services listening on port 443/tcp. After setting one up, traffic is relayed through a middleman server to the app or service running on the Dadacenter, without ever requiring an open port.

Third Party Relay (Easiest)

Cloudflare Tunnel, ngrok, and Tailscale Funnel handle the relay infrastructure for you. Zero server management, works immediately, but your traffic passes through their infrastructure.

Self-Hosted Relay (Recommended)

frp running on a cheap VPS you control. Same mechanics as third party but Traffic routes through your infrastructure.

One Hop Beyond (Networking Detour)

VPN Topology

Without further knowledge and configuration, your Dada Center is only accessible when you are within reach of its local network. While Networking Principles for DIY and Interactive Systems provides a practical anchor for these concepts, its coverage is brief. To extend this tether across mobile data carriers, external Wi-Fi, or public hotspots - you must bridge the gap between your private internal network across the public internet to your device. This is usually done through one of three practical methods depending on several conditions like:

  • Router access — can you configure NAT/port forwarding on your router?
  • User access — who is connecting, just you or the public?
  • Network stability — do you have a static public IP or does it change?
  • Protocol — are you serving HTTP/S or arbitrary TCP/UDP?

These are not mutually exclusive and a Reverse Proxy, VPN, and Reverse Cloud Tunnel can all coexist serving different purposes.

  • Use a Reverse Proxy (e.g Nginx) when you have multiple services that need to be accessible over the internet to anyone and everyone¹.

  • Use a VPN (e.g. WireGuard, Tailscale) when services only need to be accessible to you or a trusted group.

  • Use a Reverse Cloud Tunnel (e.g Cloudflare Tunnel) when your server has no true public IP Address (CGNAT) but it needs to be accessible over the internet to anyone, and/or needs to obfuscate its Public IP Address.

¹Exposing your services to the public internet to anyone and everyone means that web crawling bots will almost immediately detect your server and begin probing it by scanning for open ports, known vulnerabilities, default credentials, and outdated software versions. This happens automatically and constantly, it's not targeted or personal, the internet is just being swept by bots 24/7. So it's best practice to learn about and enforce several layers of security.

WireGuard VPN solves: How do I reach my stuff?

Reverse Proxy solves: How does the Internet reach my stuff directly?

Reverse Cloud Tunnel solves: How does the Internet reach my stuff indirectly through a third party?

Tailscale sidesteps all of this, which is why it has become popular. But convenience has a cost and Tailscale's control plane is a third party. The goal here is independent infrastructure you own, understand, and control. Tailscale is a useful tool, but it's not the destination unless you also deploy the control plane yourself.

DNS, Certs, and the $1 Fix

How are you supposed to remember that http://192.168.50.10:2283 is running Immich, http://192.168.50.10:8971 is running frigate, http://192.168.50.10:8123 is running home-assistant, and so on? It's okay to use unsecure http in your own LAN, but without TLS your login credentials, session tokens, photos, and everything else travel in plaintext. Anyone on the same network can see it. Also Modern browsers require HTTPS for anything beyond basic page rendering, and your self-hosted apps increasingly depend on those browser features.

The port problem

You're running 15 services and nobody can remember which port is which. AdGuard Home or Pi-hole let you create DNS rewrites so when you are connected to your network immich.home.arpa resolves to 192.168.50.10. Now you type a name instead of a number (http://immich.home.arpa:2283). You traded the IP for a name but still need to specify the port. A reverse proxy like NPM solves that because you configure the routing and port once in NPM, and from then on http://immich.home.arpa just works.

Which DNS server is being used to resolve addresses can be much more nuanced, varying by device, operating system, browser, and network configuration. In most cases when a device joins a network, it receives DNS server information from the router. When you visit a website such as youtube.com or instagram.com, your device will typically send the DNS query to one of those configured DNS servers first. For local names such as immich.home.arpa, this only works if the DNS server knows about those records.

The HTTPS problem

Certain apps and modern browser features are hindered without HTTPS. Even if you only plan on using them internally, HTTPS requires a TLS certificate. Let's Encrypt won't issue certs for private TLDs like .home.arpa or .lan and only validate domains that resolve publicly. So you're stuck generating and managing your own self-signed certificates across devices.

The $1 fix

Buy a real domain. A numeric .xyz is under $1/year. Now Let's Encrypt happily issues a wildcard cert for *.131043.xyz via DNS-01 challenge. Configure your own private DNS with AdGuard Home to resolve *.131043.xyz to your local IP inside your network, while public DNS servers resolve it to your public IP outside. Same URL everywhere, same cert, every browser trusts its.

Your Public Endpoint/Address on the Internet

Let's consider how most home networks are set up.

Your ISP typically provides a single device that combines several networking functions into one unit:

  • Modem – Connects to the ISP's network (cable, fiber, DSL, or cellular).
  • Router – Connects your home network to the Internet and performs Network Address Translation (NAT).
  • Switch – Provides multiple Ethernet ports for wired devices.
  • Wi-Fi Access Point – Allows wireless devices to join the network.

Devices such as computers, phones, tablets, televisions, cameras, and game consoles connect to this device either through Ethernet or Wi-Fi.

Each device receives its own private IP address (for example, 192.168.50.20, 192.168.50.50, etc.) and can communicate directly with other devices on the same Local Area Network (LAN).

When a device needs to communicate with a service on the Internet, traffic is sent to the router. The router uses Network Address Translation (NAT) to translate the device's private IP address into the home's single public IP address assigned by the ISP.

As a result, dozens or even hundreds of devices can share one public Internet connection while appearing to the outside world as a single public IP address.

VPN Topology

When you move beyond simply consuming Internet services and begin operating your own Internet infrastructure, you need a way for other systems to find you and communicate with you.

In practice, this usually requires:

  • A stable hostname (domain name) — a human-friendly name that points to your network.
  • Dynamic DNS (DDNS) — keeps that name updated when your residential public IP address changes.
  • Port Forwarding — allows incoming traffic from the Internet to reach a device inside your private network.
  • A Reverse Proxy — acts as a public entry point, terminating HTTPS connections and routing requests to the appropriate internal services.
  • A Service Listening on that Port — a web server, VPN server, or other application ready to accept connections.

Setting up a Stable Hostname with Dynamic DNS (DDNS)

  • Use a Free Duck DNS (NO CONTROL, NO PAIN)
    1. Register an account at duckdns and choose your subdomain.duckdns.org
    2. Follow the duckdns instructions for where you will run process that listens and updates your dns record.
  • Buy your own domain (TOTAL CONTROL, TOTAL PAIN)
    1. Purchase a 1.111B .xyz domain NameCheap or Porkbun. Basically any .xyz domain with 6-digit, 7-digit, 8-digit, and 9-digit numeric combinations, between ‘000000.xyz’ through ‘999999999.xyz only cost $1/year.
    2. Run ddclient in Docker and configure ddclient.conf for your provider.

Port forwarding

This requires you to familiarize yourself with your Routers Web UI Dashboard. Typically the router occupies the first usable ip in the subnet which in home networks is something like 192.168.0.1. The routers provided by ISPs are usually capable of this feature, and you will find instructions online for your specific model.

  1. Find the IP address of your Router:

    bash
    ip route | grep default
  2. For example, Navigate to http://192.168.0.1 and it's Port Forwarding page

  3. You want to forward all traffic received for the WireGuard VPN (e.g default 51820/udp) to the Datacenter (e.g 192.168.50.10).

  4. You want to forward all traffic received for port 443 for the Reverse Proxy NPM to the Datacenter (e.g 192.168.50.10). Port 80 is blocked at the ISP level, but that's okay because we shouldn't be exposing any unsecure http web apps outside our network. NPM is still able to issue certs using the DNS-01 challenge method.

Virtual-Private-Networks (VPNs)

Any device connected to the VPN joins a private, encrypted network and becomes reachable via a VPN-assigned private IP address. Your network can extend across multiple hops and geographic locations over the internet, while providing the necessary security to safely access remote systems as if they were local. Popular implementations are Wireguard, OpenVPN, IPSec, and Tailscale. In practice, Tailscale is the de-facto choice for beginners and hobbyists because it includes helpful features ready to use with minimal configuration.

VPN Topology

Wireguard is a modern VPN solution designed to create secure encrypted connections between devices across separate networks. It's the core technology behind Tailscale, but without the higher-level conveniences. You gain more independence and direct control over how your network is structured, routed, and trusted. I like to think of WireGuard as the "Skeletal VPN" that you assemble yourself, and Tailscale as the “Readymade VPN" technology.

There is no built-in concept of a server or client and every connection is peer-to-peer. The Network Topology known as hub-and-spoke resembles a client-server model because think of the central hub as the "Dadacenter" where all your Readymade Contraptions are running and the outer spokes as your clients that want secure access. This is what we'll configure in the Wireguard section.

WireGuard (TOTAL CONTROL, TOTAL PAIN)

WireGuard contains the core cryptography and tunneling logic. Tools like wg-easy wrap it in a Docker container and apply a full set of forwarding and NAT rules automatically, so it just works the moment you spin it up. Doing it yourself strips that convenience away entirely. Nothing happens by default and you have to know exactly which netfilter/iptables rules you need. This is where the TOTAL CONTROL comes from, and also where the TOTAL PAIN comes from. The goal of this section is to get a firsthand idea of that struggle, but then just use wg-easy or Tailscale instead.

Requirements

Doing it yourself means setting up a reliable address that devices can "phone home" to from anywhere on the Internet. This typically involves a domain name that is kept up to date through Dynamic DNS (DDNS), or a static Public IPv4 address. You must also configure your router or firewall to forward incoming WireGuard traffic (typically 51820/udp) to the private IP address of the WireGuard server running inside the Dadacenter.

  • A domain name updated through Dynamic DNS (DDNS), or a static Public IPv4 address
  • A UDP port open on the network edge (default: 51820/udp)
  • Port forwarding from the router/firewall to the internal WireGuard server
  • A WireGuard server running on a router, firewall, VPS, or internal host
  • Client configurations and cryptographic keys for each device
Installation
bash
sudo apt install wireguard
Key Generation

Each peer generates a private key that never leaves the machine and a public key that is shared openly. Peers encrypt traffic using each other's public key, and only the matching private key can decrypt it. The private key stays on the machine it was generated on and goes into PrivateKey in the [Interface] block. The public key is what you share with the other peer and goes into PublicKey in their [Peer] block.

bash
wg genkey | tee privatekey | wg pubkey > publickey
Server Config

The Host acting as the central hub will take on the role of a server by:

  1. Specifying the VPN subnet and the IP address it holds within it
  2. Listening on a fixed UDP port for incoming peer connections
  3. Maintaining a [Peer] block for every client and it's expected ip

Creating a server config looks something like:

sudo vim /etc/wireguard/wg0.conf

conf
[Interface]
Address = 10.0.0.1/24
PrivateKey = <server-private-key>
ListenPort = 51820
 
[Peer]
# Linux client
PublicKey = <linux-client-public-key>
AllowedIPs = 10.0.0.2/32
 
[Peer]
# Windows client
PublicKey = <windows-client-public-key>
AllowedIPs = 10.0.0.3/32
 
[Peer]
# Mobile client
PublicKey = <mobile-client-public-key>
AllowedIPs = 10.0.0.4/32

Starting the VPN session right now:

bash
wg-quick up /etc/wireguard/wg0.conf

Starting the VPN session immediately on system boot:

bash
sudo systemctl enable --now wg-quick@wg0
Client Config

When each client configures itself to communicate with the VPN server, AllowedIPs dictates which CIDR addresses are routed through the VPN tunnel. Routing only the VPN subnet and LAN (AllowedIPs = 10.0.0.0/24, 192.168.50.0/24) keeps regular internet traffic on the client's own connection and is known as "split tunneling". Routing everything through the server (AllowedIPs = 0.0.0.0/0), borrows its public IP and masks the client's own from the internet which is known as "full tunneling".

VPN Topology

The client will need to obtain the Server's Endpoint (hostname/ip:port), Server's Public Key, and their own assigned VPN IP address beforehand. This primer assumes your client will be connecting from Android/iOS or GUI app, which helps setup configuration.

Wireguard has made available a GUI application for every Operating System:

  • Windows — https://www.wireguard.com/install/
  • macOS — https://apps.apple.com/us/app/wireguard/id1451685025
  • iOS — https://apps.apple.com/us/app/wireguard/id1441195209
  • Android — https://play.google.com/store/apps/details?id=com.wireguard.android

Each client gets its own config where:

  1. The client describes itself in [interface]
  2. The server becomes the [Peer] and must have a publicly reachable IP or domain name with an open UDP port.

Create the client config (Linux): sudo vim /etc/wireguard/wg1.conf

bash
[Interface]
Address = 10.0.0.2/32
PrivateKey = <client-private-key>
DNS = 1.1.1.1
 
[Peer]
# Dadacenter server
PublicKey = <server-public-key>
Endpoint = 198.51.100.1:51820
AllowedIPs = 0.0.0.0/0
WireGuard, Docker, and IP Forwarding
  • Enable IP forwarding

    Normally a host will not behave as a router because it only cares about messages that it is the destination for, dropping anything addressed to somewhere else. Enabling IP forwarding changes this default, telling the kernel to pass traffic through to another interface instead of discarding it.

    bash
    sudo sysctl -w net.ipv4.ip_forward=1
  • Add NAT/forwarding rules to the server config itself. This is the part wg-easy gives you for free, but a manual config lacks unless you add it. Without this, clients can reach 10.0.0.1 and nothing past it.

    wg0-server.conf
    [Interface]
    Address = 10.0.0.1/24
    PrivateKey = <server-private-key>
    ListenPort = 51820
    PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
     
    [Peer]
    # Linux client
    PublicKey = <linux-client-public-key>
    AllowedIPs = 10.0.0.2/32

    When the VPN goes up, PostUp = iptables -A FORWARD -i wg0 -j ACCEPT is needed to allow traffic arriving from the WireGuard interface to be forwarded onward to its destination, rather than dropped by the host. iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE is needed to rewrite that traffic's source address to the host's own IP before it leaves through eth0, so replies from the internet know how to find their way back to a private VPN client address that the outside world has no route to otherwise. Without them, traffic from connected clients either get silently dropped at the host before it ever leaves, or it leaves with a private source address the internet has no way to send a reply back to. PostDown simply undoes those changes when the VPN is brought down.

wg-easy (TOTAL CONTROL, LESS PAIN)

wg-easy Topology

WireGuard-Easy allows you to manage the WireGuard VPN through a Web-based Admin UI. Instead of manually creating keys, editing configuration files, and troubleshooting firewall rules, new VPN clients can typically be created with a few clicks from the web interface. It automatically generates client configurations and QR codes, allowing phones, laptops, and tablets to connect with minimal setup.

Installation
docker-compose.yaml
volumes:
  etc_wireguard:
 
services:
  wg-easy:
    environment:
    #  Optional:
    #  - PORT=51821
    #  - HOST=0.0.0.0
      - INSECURE=true
 
    image: ghcr.io/wg-easy/wg-easy:15
    container_name: wg-easy
    networks:
      wg:
        ipv4_address: 10.42.42.42
        ipv6_address: fdcc:ad94:bacf:61a3::2a
    volumes:
      - etc_wireguard:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
      # - NET_RAW # ⚠️ Uncomment if using Podman
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv6.conf.all.forwarding=1
      - net.ipv6.conf.default.forwarding=1
 
networks:
  wg:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 10.42.42.0/24
        - subnet: fdcc:ad94:bacf:61a3::/64

After docker compose up -d, the Admin UI is accessible in the example network at http://192.168.50.10:51821/

How Peers Find You
wg-easy01

You have to specify what address and port gets baked into every generated peer config so peers can initiate a connection to your WireGuard server. This requires:

  • A stable hostname — a domain name that points to your network's public IP.
  • Dynamic DNS (DDNS) — keeps that hostname updated when your residential public IP changes.
  • Port forwarding — a rule on your router that forwards incoming traffic on the WireGuard port (default: 51820/udp) to the correct host on your LAN.
Peer Configuration
wg-easy02

Generating new peer configurations and distributing them becomes easier. Every new peer created will allow you to export a file to share for other Wireguard Clients to quickly import and use. The same confioguration file is encoded into a QR Code that your WireGuard VPN smartphone app will quickly recognize. Then connecting and disconnecting to your VPN is a simple as toggling a switch on/off.

Tailscale (NO CONTROL, NO PAIN)

Tailscale will quickly set you up with a secure peer-to-peer mesh VPN using the WireGuard protocol and a Coordination Server to handle Key Exchange between Peers, Identity and Authenication through OAuth/SSO, Fallback Relay coordination (DERP Servers), and Endpoint discovery. Tailscale is known as "open-core" because the Tailscale clients (the daemon that runs on each node) are open source, but the coordination server (controlplane.tailscale.com) is proprietary and provides a few more capabilities and conveniences. Headscale is an alternative community made open-source coordination server to run on your own infrastructure.

Reverse-Proxies

A Reverse Proxy is an "intermediary server" that sits between clients and the actual services they are trying to reach. It can be used a few different ways such as a Load Balancer to spread traffic across multiple duplicate servers, a Cache for static content to reduce load on servers, and Transport Layer Security (TLS) so backend servers don't have to handle encryption themselves. They allow multiple services (e.g Immich, ArchiveBox, Nextcloud) to be accessed via one port (443) using subdomains. They obfuscate the IP Addresses of the servers behind them, hiding the real backend IPs from clients.

Nginx/Nginx Proxy Manager

Nginx Proxy Manager (NPM) allows you to manage Nginx through a Web-based Admin UI.

When I first set up NPM, I did what everyone does which is to publish a port on every container and point NPM at the <host IP>:port - it works but it's less secure, and clutters up your host's firewall ports. This is because the simplest approach to get something running is to create a docker-compose.yaml and run docker compose up -d, which automatically places containers on a project-specific default network and encourages publishing ports to the host.

The cleaner move is to create a shared proxy Docker network for NPM and attach only the containers that need forwarding to it, while keeping the rest on their default project network. This only requires you to add a few lines to each docker-compose.yaml. A similar approach is encouraged in the npm documentation, but we don't add ALL containers to the shared network.

npm reverse proxy
Installation
  1. Create a proxy network for nginx proxy manager and Readymade Logic to cooperate on:
bash
docker network create npm_network
  1. Edit the NPM compose to use the npm_network.
docker-compose.yaml
services:
  app:
    image: 'jc21/nginx-proxy-manager:2.15.1'
    restart: unless-stopped
    environment:
      TZ: "Australia/Brisbane"
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt    
    networks:
      - default
      - npm_network
 
networks:
  npm_network:
    external: true

After docker compose up -d, the NPM Admin UI is accessible in the example network at http://192.168.50.10:81/

  1. Modify each Readymade Logic's docker-compose.yaml to attach the main container to both the proxy network and the project-specific default network. You will need to add a networks: block declaring the external network, and then specify which container to use it. You must also include - default explicitly, so that the container is attached to both their default project network and the npm_proxy network. You will also no longer need to Publish ports because npm is able to directly route traffic using the proxy network. For example, the Immich compose becomes:
docker-compose.yaml
name: immich
 
services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    volumes:
      - ${UPLOAD_LOCATION}:/data
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
  # ports:           # No longer need to Publish Ports to the Docker host
  #   - '2283:2283'  # NPM can route using http://immich_server:2283
    depends_on:
      - redis
      - database
    restart: always
    healthcheck:
      disable: false
    networks:
      - default # internal immich network (db, redis, ml)
      - npm_network   # shared network with NPM
 
  immich-machine-learning:
    container_name: immich_machine_learning
    ......NO CHANGE
 
  redis:
    container_name: immich_redis
    ......NO CHANGE
 
  database:
    container_name: immich_postgres
    ......NO CHANGE
 
volumes:
  ......NO CHANGE
  
networks:
  npm_network:
    external: true
Creating a Certificate and Adding Proxy Forwarding
npm_01a

Use the Navigation Menu to go to the Proxy Hosts page (Hosts > Proxy Hosts) and Add Proxy Host. Based on our immich example we will make https://immich.12419251.xyz available on the internet and route our traffic to our immich_server container.

We use the wild certificate supporting all subdomains *.12419251.xyz for immich.12419251.xyz and

npm_01b
npm_02

Use the Navigation Menu to go the Certificates page and Add Certificate > Let's Encrypt via DNS. Whichever domain registrar or DNS provider you end up with will provide an API key for their DNS service. NPM needs this to complete the DNS-01 challenge when issuing wildcard certificates (*.12419251.xyz) from Let's Encrypt. Why a wildcard? Without one, you'd need to request a separate certificate for every subdomain (e.g immich.131043.xyz, frigate.131043.xyz, and so on. A wildcard cert covers all of them with a single issuance.

Reverse Cloud Tunnels

Reverse Cloud Tunnels make your services available without publicly exposing ports on your server. They are a work-around for restrictive firewalls (e.g ISPs, Corporate, CGNAT) by leveraging outbound traffic leniency on tcp/443 (HTTPS) to establish a persistent connection to a cloud relay like Cloudflare Edge Server that accepts and forwards inbound public traffic back through the pre-established tunnel to your local service.

Cloudflare Tunnels, Tailscale Funnels will automatically handle HTTPS/TLS on your assigned domain but will vary in how much control you have over that domain name. Cloudflare allows you to use your owned domain (e.g https://123456789.xyz) but also provides ephemeral temporary domains like https://adjective-noun-verb-word.trycloudflare.com. Tailscale Funnels is locked to *.ts.net and only assigns your service to hostname.tailnetname.ts.net/appname (e.g https://dadacenter.tail6234.ts.net/immich).

In the Nginx Reverse Proxy example, we forwarded requests to the Dadacenter at 192.168.50.10:443 through an open port on the home router. A reverse tunnel eliminates that requirement. Instead of opening a port, a process on your LAN maintains an outbound connection to a VPS relay, and clients access services on your LAN from there. Since these tunnels expose one port per tunnel, without Nginx or something like it, you'd need a separate tunnel for every service. For the following examples, we've already set up Nginx Proxy Manager, so we only use 1 tunnel on port 443/tcp and let NPM multiplex traffic to its destination.

Cloudflare Tunnel

To use Cloudflare Tunnel with your own domain, you first need to delegate DNS to Cloudflare by pointing your registrar's nameservers (e.g Namecheap, Porkbun, etc) at Cloudflare's. Then setting up Cloudflare to become the authoritative DNS.

cloudflared route setup
  1. Create a tunnel
    • Cloudflare will provide you instructions to setup cloudflared - The Cloudflare Tunnel Daemon and have it automatically hook into cloudflare infrastructure. You can run it as a systemd unit or as a container.
    bash
    docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token ******
  2. Publish an application
    • We are using the wildcard subdomain *.12419251.xyz and the Service URL is the address of NPM running on the dadacenter https://192.168.50.10:443
cloudflared route setup
  1. Update origin parameters

    • Match SNI to Host needs to be true
  2. Maintain NPM using proxy hosts with the wildcare certificate

Tailscale Funnel

If you are already using Tailscale as your VPN, Tailscale Funnel becomes the shortcut from private service to public URL. It's much simpler to use it as intended. It is simplest when used as intended: exposing one local service through one public Tailscale hostname.

A Funnel URL looks like this:

https://dadacenter.tail6234.ts.net/

Apps like Frigate, Home Assistant, Immich, and many others can sometimes be placed under subpaths:

code
https://dadacenter.tail6234.ts.net/frigate
https://dadacenter.tail6234.ts.net/immich
https://dadacenter.tail6234.ts.net/homeassistant

But this is where things get fragile.

Many self-hosted apps assume they live at the root of a site. Their frontend code, API calls, login redirects, websocket connections, video streams, and static assets may point to paths like:

code
/api 
/ws 
/live

instead of:

code
/frigate/api 
/frigate/ws 
/frigate/live

Some applications support being hosted under a subpath by letting you configure a base URL, public URL, root path, or URL prefix. When that option exists, the app knows to generate paths like /frigate/api instead of /api, so subpath routing can work cleanly.

frp

Fast Reverse Proxy (frp)

frp relay

You need your domains authoritative dns to have an A Record pointing any subdomain *.12419251.xyz to the ipv4 address of your vps (e.g 198.51.100.25)

With frp you run frpc on the client and frps on the relay vps.

  1. Create a dedicated non-privileged user to run frps
bash
sudo useradd --system --no-create-home --shell /usr/sbin/nologin frps
  1. Run frps as a systemd service
bash
sudo vim /etc/systemd/system/frps.service

We want to access our immich server with https://immich.12419251.xyz instead of https://immich.12419251.xyz:1443. Port 443 is privileged port, which means only the root user or processes with administrative privileges can listen on them. So we create the frps user and invoke AmbientCapabilities andCapabilityBoundingSet in the frps.service to work around this.

frps.service
[Unit]
Description=frp server
After=network.target
 
[Service]
Type=simple
User=frps
ExecStart=/opt/frp/frps -c /opt/frp/frps.toml
Restart=on-failure
RestartSec=5s
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
 
[Install]
WantedBy=multi-user.target
frps.toml
bindPort = 7000
bash
sudo systemctl daemon-reload
sudo systemctl enable --now frps
sudo systemctl status frps
  1. Create a dedicated non-privileged user to run frpc
bash
sudo useradd --system --no-create-home --shell /usr/sbin/nologin frpc
  1. Run frpc as a systemd service
bash
sudo vim /etc/systemd/system/frpc.service
frpc.service
[Unit]
Description=frp client
After=network.target
 
[Service]
Type=simple
User=frpc
ExecStart=/opt/frp/frpc -c /opt/frp/frpc.toml
Restart=on-failure
RestartSec=5s
 
[Install]
WantedBy=multi-user.target
frpc.toml
serverAddr = "198.51.100.25" # ipv4 address of vps running frps 
serverPort = 7000 # the bindPort setup in the relay vps frps
 
[[proxies]]
name = "npm-tcp"
type = "tcp"
localIP = "127.0.0.1" # our loopback localhost address since we are running npm on the same host
localPort = 443 # the port npm is using for https
remotePort = 443 # the port clients have to use, recall the port 1443 avoidance in the frps section
bash
sudo systemctl daemon-reload
sudo systemctl enable --now frpc
sudo systemctl status frpc

Readymade Models (WIP)

Large Language Models (LLMs) are the core technology augmenting how we read, write, search, and reason. The proprietary frontier models such as the latest GPT, Claude, Gemini, and Grok represent the avant-garde of this technology. Each one is accessed remotely through metered APIs, and billed by the token for every word they read and every word they write. LocalLLMs are powerful open-weighted models, meaning that the labs that trained them released the weights publicly, and you can run them on your own hardware. Hugging Face has become the registry for open-weights LLMs, hosting models such as DeepSeek, Qwen, Llama, Gemma, and Mistral. Anyone can download them, and enthusiasts running their own LLMs are demonstrating that gaming GPUs can be enough in certain use cases to assist in vibe-coding and putting the model to work autonomously. Harnessing these models in the right environment, equipped with tools to use, memory to draw on, and a workspace to act in, allows them to punch up in their capabilities.

  • Token — The unit a model reads and writes. Roughly one token per ~4 characters of English text. Not the same as a word — "unbelievable" is three tokens, "the" is one. Every model has a maximum tokens-per-second throughput and is billed (in the case of frontier models) per token in and out.
  • Context window — How much text the model can hold in its working memory at once, measured in tokens. A 128K context window means the model can see roughly 96,000 English words in one conversation before older content gets dropped. Frontier models in 2026 have 1M-10M context windows. Local models typically have 8K-256K.
  • Parameters (the "B" number) — The trained weights of the model, counted in billions. 7B means seven billion parameters. Bigger = smarter, generally, but also bigger = more VRAM required. A 7B model fits on an 8GB GPU. A 70B model needs 48-64GB. A 400B+ model needs a multi-GPU server.
  • Quantization — A compression technique that reduces the precision of the weights to make the model smaller. A model in Q4 (4-bit) quantization is roughly one-quarter the size of the original FP16 weights, with a small quality cost. This is the technique that makes local LLMs viable on consumer hardware. Q4_K_M and Q5_K_M are the most common formats.
  • VRAM — Video RAM, the memory on your GPU. The single most important hardware constraint for local LLMs. A model's quantized file size is approximately the VRAM it needs to run.
    • NVIDIA GeForce RTX 3060 12GB — Runs 7B-14B comfortably. Good for chat, drafting, and light coding help.
    • NVIDIA GeForce RTX 3090 24GB — Runs 27B-35B comfortably. The entry point for driving agents on your own hardware.

Initial setup requires 3 swappable layers, often bundled together.

  1. The Model — the weights, downloaded as a file.
  2. The Inference Engine — the runtime that loads the weights and runs the math.
  3. The Gateway — the HTTP server that exposes the engine to other programs

The three tools below each handle these in different ways. Ollama and Docker Model Runner bundle all three into a single command. llama.cpp is the bare inference engine the other two are built on; running it directly means assembling the model, the engine, and the gateway yourself.

Ollama (WIP)

Ollama

Docker Model Runner (WIP)

Docker Model Runner

llama.cpp (WIP)

llama.cpp

Readymade Agents (WIP)

Whether you are setting up an LLM locally or tapping into a subscription or token-based cloud provider, what you want is an OpenAI-compatible API that can serve as the engine driving your autonomous agents. LLMs primarily started as a request-and-response interaction but have quickly evolved into feedback loops where the response also becomes the request. OpenClaw became the first Readymade self-hosted agentic platform demonstrating that a personal AI agent could live entirely on your own hardware and be customized to the specific shape of your own absurdity.

Readymade agent platforms like Claude Code, Codex, Hermes, OpenClaw, OpenCode, and similar tools are built around the same basic idea: give a language model a workspace, a goal, tools, memory, and permission to act. The model stops being only a chatbot and becomes a cybernetic loop that observes, plans, edits, runs commands, reads the result, corrects itself, and continues.

Although each platform has its own interface and philosophy, they tend to share a few common layers:

  • Markdown Interface — Markdown is the common surface between human and agent: instructions, plans, notes, docs, summaries, and task outputs all become readable, editable text.

  • Prompt Hierarchy — Agents are shaped by layered instructions: system rules, project rules, skills, and the user’s immediate request. This determines what the agent should prioritize.

  • Workspace Context — The agent is attached to a real project or environment. It can inspect files, search code, read documentation, notice conventions, and adapt its work to the existing system.

  • Tool Use — The model can reach outside itself through shell commands, file edits, browsers, APIs, package managers, test runners, databases, and local services.

  • Skills and Plugins — Reusable bundles of instructions and tools let agents specialize. A skill might teach browser testing, image generation, code review, deployment, or a project-specific workflow.

  • Action Loop — The agent observes, plans, acts, reads the result, corrects itself, and continues. This feedback loop is what separates an agent from a chatbot.

  • Permissions and Sandboxing — Because agents can affect real systems, platforms usually control access to files, networks, secrets, installs, commits, deploys, and destructive commands.

The important shift is that these systems make agency itself into readymade logic. You are no longer writing every command by hand. You are steering an evolving substrate where an LLM can reach downward through tools into the operating system, the repository, the browser, the container stack, and eventually the rest of your infrastructure, and beyond.

Hermes (WIP)

Hermes Agent

OpenCode (WIP)

OpenCode

Security (WIP)

3-2-1 Backup (WIP)

Beneath the Dadacenter (WIP)