{
  "path": "/3m3zys4gkfscf",
  "site": "at://did:plc:ofrbh253gwicbkc5nktqepol/site.standard.publication/3m3x4bgbsh22k",
  "tags": [
    "apple",
    "game",
    "infra"
  ],
  "$type": "site.standard.document",
  "title": "Hosting My Own Minecraft Server: Replacing Aternos with a Mac Mini",
  "content": {
    "$type": "pub.leaflet.content",
    "pages": [
      {
        "$type": "pub.leaflet.pages.linearDocument",
        "blocks": [
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 44,
                    "byteStart": 37
                  },
                  "features": [
                    {
                      "uri": "https://aternos.org",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "After several months of dealing with Aternos with the queues, the sluggishness, the general sense of unreliability, I decided to host my own Minecraft server. I already had an M2 Mac mini being my main computer and a background in breaking things until they work. This seemed like a reasonable use of time."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Why I Gave Up on Aternos"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Aternos is fine. It does what it says. It’s free. But once the novelty of “free server hosting” wears off, the cracks start to show. The 10-minute queue before you can even start your own server is tolerable the first few times, but quickly becomes a chore. Friends couldn’t just jump in either, they had to wait for me to start it manually, usually after texting to ask why it wasn’t online. Which, to be fair, is a reasonable question."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Performance was inconsistent. Some days it worked. Other days it choked under basic load. The setup just didn’t suit how we play — irregularly, impulsively, and often without warning."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "The Stack (Probably Overthought)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 71,
                    "byteStart": 62
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 98,
                    "byteStart": 87
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "I didn’t go the quick route. What should have been a single java -jar became this... monstrosity:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "plaintext",
              "plaintext": "mc.ewancroft.uk\n     ↓\n Cloudflare\n     ↓\n TCPShield\n     ↓\n DuckDNS\n     ↓\n Home Router\n     ↓\n Docker Container"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Whether this is overengineered or mildly practical depends on your perspective. It works, and I haven't accidentally exposed the Mac to the entire internet (yet), so I'll take that as a success."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Custom Domain"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 34,
                    "byteStart": 19
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "The server runs at mc.ewancroft.uk because I already own the apex domain and didn’t want to mess with public IPs or weird third-party subdomains. It's clean, it works, and it's easier to remember than a string of numbers."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "TCPShield"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "DDoS protection on a private server for ~10 people is obviously overkill, but TCPShield has a free tier, and it integrates cleanly with Cloudflare. It probably isn’t doing much, but it looks professional, and the setup process was surprisingly painless."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "DuckDNS"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "My IP address changes on a whim, so DuckDNS handles dynamic DNS. A tiny script checks for changes every five minutes. I keep expecting it to break or rate-limit me, but so far it hasn’t. That alone feels suspicious, but I’m not going to complain."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Port Forwarding"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 34,
                    "byteStart": 29
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Standard router config: port 25565 forwarded to the Mac. No insights here. Just a bit of tinkering, a few reboots, and the usual moment of panic when the router UI stops responding."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Docker Container"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 66,
                    "byteStart": 45
                  },
                  "features": [
                    {
                      "uri": "https://github.com/itzg/docker-minecraft-server",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "This bit went smoother than expected. I used itzg/minecraft-server to containerise everything. Running the server in Docker means less hassle with system dependencies and makes it easier to roll back if something breaks."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The image supports plugins, config tweaks, and version switching with minimal idiocy. For what it’s worth, I would do this part the same way again."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Plugins and Preferences"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The server runs PaperMC 1.21.1 with 4GB of RAM. That’s been enough for our group."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Plugins:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "ViaVersion / ViaBackwards (to support laggards on older clients)"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "AxGraves (keeps items after lava-based deaths)"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "TreeFeller / VeinMiner (cut corners without shame)"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Backuper (because I don’t trust myself)"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Chunky (to avoid terrain loading spikes mid-session)"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 34,
                    "byteStart": 6
                  },
                  "features": [
                    {
                      "uri": "https://modrinth.com/resourcepack/bare-bones",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "I use the Bare Bones resource pack. It’s clean, readable, and doesn’t get in the way. The whitelist keeps out strangers, which is good, because I haven’t secured this to enterprise-level production standards and don’t intend to."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Preventing Sleep"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The Mac mini likes to sleep when left unattended. A few simple scripts handle this:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 10,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "caffeinate runs while the server is up"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "When the server stops, the system is allowed to sleep again"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "It works. I’ve resisted the urge to add more logic, mostly because I know I’ll break it."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Not a 24/7 Server"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "This isn’t a 24/7 setup. It’s started manually when we want to play, and shut down when we’re done. That’s usually at odd hours (like 20:43, 00:17, 14:52) whenever someone feels the need to terraform a mountain or test a new datapack."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "My typical group is entirely neurodivergent. Planning ahead is rarely an option. Everything’s spontaneous, which means the server needs to be easy to bring online with short notice - and easy to forget about for a week without something catching fire."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "It works for us."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "What Improved"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Compared to Aternos:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "No startup delay"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "More stable performance"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Full control over settings and plugins"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Backups that actually happen"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "A proper domain instead of a random link"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "No shutdowns unless I choose to"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "What I Learned"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Layered setups aren’t always overkill, but they do get complex fast"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Docker simplifies things that would otherwise be painful"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Automation is worth the time it saves later"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "A subdomain makes the whole thing feel vaguely official"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "More importantly, I realised I actually enjoy this kind of work. Infrastructure tinkering was never the plan, but here we are."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "What’s Next"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "I’m considering:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Dedicated low-power hardware"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Offsite backups (because I’ve had enough close calls)"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Better monitoring (even if I’ll mostly ignore it)"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Multiple server instances for different world types"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Or maybe I’ll just leave it as-is and play when we feel like it. That’s probably more realistic."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Closing"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Moving off Aternos was the right call. The new setup isn’t perfect, but it’s fast, stable, and completely mine - which means when it breaks, I know exactly who to blame."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "If you’ve got spare hardware and don’t mind a bit of trial-and-error, hosting your own server is absolutely worth a go. Just keep the scope small, or you’ll wake up at 03:00 wondering whether your DNS settings broke TCPShield again."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 42,
                    "byteStart": 36
                  },
                  "features": [
                    {
                      "uri": "https://github.com/ewanc26/mc-server",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "All scripts and config files are on GitHub if you're curious - or if you want a working setup that includes several questionable decisions."
            }
          }
        ]
      }
    ]
  },
  "publishedAt": "2025-06-11T00:53:26.728Z"
}