How to Set Up a Minecraft Server on FreeBSD
FreeBSD is one of the best platforms for running a Minecraft Java Edition server, and that is not an exaggeration. The combination of ZFS for instant world backups, jails for process isolation, PF for network security, and rock-solid stability under sustained load makes it a genuinely superior choice to spinning up yet another Ubuntu VPS and hoping for the best.
This guide walks through every step: installing Java, downloading the server, configuring server.properties, writing a proper rc.d service script, running the server inside a jail, tuning performance, setting up automated ZFS backups, and locking things down with PF firewall rules. By the end, you will have a production-grade Minecraft server that starts on boot, backs up automatically, and does not fall over when twenty players decide to load new chunks simultaneously.
Why FreeBSD for a Minecraft Server
Most Minecraft server guides assume Linux. That is fine, Linux works. But FreeBSD offers several concrete advantages for this specific workload:
ZFS snapshots for world backups. Minecraft worlds are large, growing directories of region files. Traditional backup approaches involve stopping the server, copying gigabytes of data, and restarting. With ZFS, you take an atomic snapshot in milliseconds -- no server downtime, no inconsistent state, no half-written chunk files. You can roll back to any snapshot instantly if a griefer destroys your spawn or a plugin corrupts chunk data.
Jails for isolation. Running a Minecraft server means running a JVM that accepts network connections from the public internet. That is a significant attack surface. FreeBSD jails let you isolate the entire Minecraft server -- Java runtime, server jar, world data, plugins -- in a lightweight container with its own filesystem, network stack, and resource limits. If something goes wrong, the blast radius stays inside the jail.
Stability under load. FreeBSD's network stack and scheduler handle sustained, long-running workloads well. A Minecraft server runs 24/7, processing ticks, handling player connections, and managing disk I/O for chunk loading. FreeBSD does not surprise you with unattended upgrades, kernel updates that require reboots, or package manager conflicts breaking your Java installation at 3 AM.
PF firewall. The PF packet filter is clean, readable, and powerful. Restricting access to your Minecraft port while allowing SSH for administration takes a few lines of configuration, not a maze of iptables chains.
If you are already running FreeBSD for other services -- a NAS, a web server, a development environment -- adding a Minecraft server is a natural fit. If you are new to FreeBSD, a Minecraft server is actually a solid first project because it exercises many core FreeBSD features (packages, services, ZFS, jails, PF) in a context that is immediately satisfying to test.
Prerequisites
You need:
- FreeBSD 14.0 or later (14.2 recommended)
- At least 2 GB of RAM dedicated to the Minecraft server (4 GB or more recommended for 10+ players)
- A ZFS pool with enough free space for world data (20 GB minimum, 50 GB comfortable)
- Root or sudo access
- A static IP address or properly forwarded port 25565
This guide assumes a clean FreeBSD system with ZFS as the root filesystem, which is the default for modern FreeBSD installations.
Step 1: Install Java
Minecraft Java Edition requires a Java runtime. As of Minecraft 1.21+, Java 21 is the minimum supported version. Install OpenJDK 21 from packages:
shpkg install openjdk21
Verify the installation:
shjava -version
You should see output like:
shellopenjdk version "21.0.5" 2025-10-14 OpenJDK Runtime Environment (build 21.0.5+11-FreeBSD) OpenJDK 64-Bit Server VM (build 21.0.5+11-FreeBSD, mixed mode, sharing)
If you have multiple Java versions installed, set the default by updating the symlink:
shln -sf /usr/local/openjdk21/bin/java /usr/local/bin/java
Step 2: Create a Dedicated User
Never run a Minecraft server as root. Create a dedicated user with a home directory for the server files:
shpw useradd -n minecraft -d /home/minecraft -m -s /usr/sbin/nologin
The -s /usr/sbin/nologin shell prevents interactive login, which is good security practice for service accounts. You will interact with the server through screen or tmux, not by logging in as the minecraft user directly.
Create the server directory:
shmkdir -p /home/minecraft/server chown minecraft:minecraft /home/minecraft/server
Step 3: Download the Server Jar
Download the latest Minecraft server jar from the official Mojang servers. As of early 2026, version 1.21.4 is current:
shcd /home/minecraft/server fetch https://piston-data.mojang.com/v1/objects/4707d00eb834b446575d89a61a11b5d548d8c001/server.jar chown minecraft:minecraft server.jar
The download URL changes with each version. Check the official Minecraft server download page for the current link, or use a tool like mcversions.net to find the direct URL.
Step 4: Accept the EULA
Minecraft requires you to accept the End User License Agreement before the server will start. You can either run the server once (it will generate eula.txt and exit) or create the file directly:
shecho "eula=true" > /home/minecraft/server/eula.txt chown minecraft:minecraft /home/minecraft/server/eula.txt
By creating this file, you are agreeing to the Minecraft EULA at https://aka.ms/MinecraftEULA. Read it before proceeding.
Step 5: Configure server.properties
The first time the server runs, it generates a server.properties file with defaults. You can also create one in advance. Here is a walkthrough of the key settings:
properties# server.properties - Key settings explained # Server network settings server-port=25565 server-ip= # Leave server-ip blank to bind to all interfaces, or set a specific IP # World settings level-name=world level-seed= # Leave seed blank for random, or specify a seed string level-type=minecraft\:normal # Options: normal, flat, large_biomes, amplified, single_biome_surface # Game settings gamemode=survival difficulty=normal hardcore=false pvp=true max-players=20 spawn-protection=16 # Radius around spawn where only ops can build # Performance-critical settings view-distance=10 simulation-distance=10 # Lower these if you have limited CPU or RAM # view-distance: how far players can see (chunks) # simulation-distance: how far entities tick (chunks) # Network settings network-compression-threshold=256 # Compress packets larger than this many bytes (-1 to disable) max-tick-time=60000 # Watchdog kills the server if a tick takes longer than this (ms) # Set to -1 to disable (useful during world generation) # Security white-list=false enforce-whitelist=false online-mode=true # online-mode=true verifies players against Mojang servers # NEVER set to false on a public server # RCON (remote console) enable-rcon=false rcon.password= rcon.port=25575 # Enable only if you need remote administration # Miscellaneous motd=A FreeBSD Minecraft Server enable-command-block=false allow-flight=false spawn-monsters=true spawn-animals=true generate-structures=true
The two settings that matter most for performance are view-distance and simulation-distance. On a server with 2 GB of RAM and 10 players, set both to 8. On a server with 4+ GB and fewer players, 10-12 works well. Going above 14 is rarely worth the CPU cost.
Step 6: First Start (Manual Test)
Before creating a service script, verify everything works with a manual start:
shsu -m minecraft -c "cd /home/minecraft/server && java -Xms1G -Xmx2G -jar server.jar nogui"
The -Xms1G flag sets the initial heap size and -Xmx2G sets the maximum. The nogui argument skips the graphical console (which you do not want on a headless server).
Watch the output for errors. The first start takes longer as the server generates the initial world spawn area. Once you see Done! For help, type "help", the server is running. Type stop in the console to shut it down cleanly.
Step 7: Create an rc.d Service Script
A proper rc.d script lets you manage the Minecraft server like any other FreeBSD service -- service minecraft start, service minecraft stop, automatic startup on boot.
Create the file /usr/local/etc/rc.d/minecraft:
sh#!/bin/sh # PROVIDE: minecraft # REQUIRE: LOGIN DAEMON NETWORKING # KEYWORD: shutdown . /etc/rc.subr name="minecraft" rcvar="minecraft_enable" load_rc_config $name : ${minecraft_enable:="NO"} : ${minecraft_user:="minecraft"} : ${minecraft_dir:="/home/minecraft/server"} : ${minecraft_jar:="server.jar"} : ${minecraft_xms:="1G"} : ${minecraft_xmx:="2G"} : ${minecraft_jvm_flags:="-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1"} : ${minecraft_pidfile:="/var/run/minecraft.pid"} : ${minecraft_logfile:="/var/log/minecraft.log"} pidfile="${minecraft_pidfile}" command="/usr/sbin/daemon" command_args="-f -p ${pidfile} -o ${minecraft_logfile} -u ${minecraft_user} /usr/local/bin/java -Xms${minecraft_xms} -Xmx${minecraft_xmx} ${minecraft_jvm_flags} -jar ${minecraft_dir}/${minecraft_jar} nogui" start_precmd="minecraft_prestart" stop_cmd="minecraft_stop" status_cmd="minecraft_status" extra_commands="console" console_cmd="minecraft_console" minecraft_prestart() { cd "${minecraft_dir}" || return 1 if [ ! -f "${minecraft_dir}/${minecraft_jar}" ]; then echo "Server jar not found: ${minecraft_dir}/${minecraft_jar}" return 1 fi if [ ! -f "${minecraft_dir}/eula.txt" ]; then echo "eula.txt not found. Create it with: echo 'eula=true' > ${minecraft_dir}/eula.txt" return 1 fi touch "${minecraft_logfile}" chown "${minecraft_user}" "${minecraft_logfile}" return 0 } minecraft_stop() { if [ -f "${pidfile}" ]; then _pid=$(cat "${pidfile}") # Send the Minecraft 'stop' command by killing the daemon gracefully echo "Stopping Minecraft server (PID: ${_pid})..." kill -TERM "${_pid}" 2>/dev/null # Wait for graceful shutdown (world save) _timeout=30 while [ ${_timeout} -gt 0 ] && kill -0 "${_pid}" 2>/dev/null; do sleep 1 _timeout=$((_timeout - 1)) done if kill -0 "${_pid}" 2>/dev/null; then echo "Server did not stop gracefully, forcing..." kill -KILL "${_pid}" 2>/dev/null fi rm -f "${pidfile}" echo "Minecraft server stopped." else echo "Minecraft server is not running." fi } minecraft_status() { if [ -f "${pidfile}" ] && kill -0 "$(cat "${pidfile}")" 2>/dev/null; then echo "Minecraft server is running (PID: $(cat "${pidfile}"))." else echo "Minecraft server is not running." return 1 fi } minecraft_console() { echo "Tailing Minecraft server log (Ctrl+C to exit):" tail -f "${minecraft_logfile}" } run_rc_command "$1"
Set permissions and enable the service:
shchmod 755 /usr/local/etc/rc.d/minecraft sysrc minecraft_enable="YES"
You can also override defaults in /etc/rc.conf:
shsysrc minecraft_xmx="4G" sysrc minecraft_xms="4G" sysrc minecraft_dir="/home/minecraft/server"
Now manage the server with standard commands:
shservice minecraft start service minecraft stop service minecraft restart service minecraft status
Step 8: Memory Allocation and JVM Flags
The JVM flags in the rc.d script above are the Aikar flags, which are the gold standard for Minecraft server JVM tuning. Here is what the key flags do:
-Xmsand-Xmx: Set these to the same value. This pre-allocates all memory at startup and avoids heap resizing during gameplay. If you have 4 GB to give the Minecraft server, use-Xms4G -Xmx4G.-XX:+UseG1GC: Uses the G1 garbage collector, which is far better for Minecraft's allocation patterns than the default collector.-XX:MaxGCPauseMillis=200: Tells G1 to aim for GC pauses under 200 ms, reducing tick lag spikes.-XX:G1NewSizePercent=30andG1MaxNewSizePercent=40: Allocates 30-40% of the heap to the young generation, matching Minecraft's pattern of creating many short-lived objects per tick.-XX:+AlwaysPreTouch: Pages all memory at startup rather than lazily on first use. Adds a few seconds to startup time but avoids page faults during gameplay.
How much RAM do you need? As a rough guide:
| Players | RAM | view-distance |
|---------|-----|---------------|
| 1-5 | 2 GB | 10 |
| 5-10 | 3 GB | 10 |
| 10-20 | 4-6 GB | 8-10 |
| 20-40 | 6-10 GB | 6-8 |
Never allocate more than 12 GB to a single Minecraft server instance. Beyond that, GC pauses become long enough to cause noticeable lag regardless of tuning. If you need to support more players, run multiple servers behind a proxy like Velocity.
Step 9: Running in a Jail
For proper production isolation, run the Minecraft server inside a FreeBSD jail. This limits what a compromised server process can access and lets you set hard resource limits.
Create a jail for the Minecraft server:
sh# Create the jail filesystem zfs create zpool/jails/minecraft
Set up a basic jail configuration in /etc/jail.conf:
shellminecraft { host.hostname = "minecraft.jail"; ip4.addr = "lo1|10.0.0.2/24"; path = "/jails/minecraft"; mount.devfs; exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; allow.raw_sockets; persist; }
Install the base system and Java inside the jail:
shbsdinstall jail /jails/minecraft pkg -j minecraft install openjdk21
Then copy your server files into the jail and set up the rc.d script inside it. The jail sees its own filesystem root, its own network interface, and nothing else on the host.
Resource Limits with rctl
Use rctl to set hard resource limits so the Minecraft server cannot consume all system resources:
sh# Limit the jail to 4 GB of RAM rctl -a jail:minecraft:memoryuse:deny=4G # Limit to 2 CPU cores worth of processing rctl -a jail:minecraft:pcpu:deny=200 # Limit the number of processes (prevents fork bombs) rctl -a jail:minecraft:maxproc:deny=100
Make sure kern.racct.enable=1 is set in /boot/loader.conf and reboot for rctl to work.
These limits are hard ceilings. If the JVM tries to allocate more than 4 GB, the allocation fails rather than starving other services on the host. This is particularly important if you run multiple services on the same machine.
Step 10: Performance Tuning
Use Paper or Purpur Instead of Vanilla
The vanilla Minecraft server is not optimized. For any server with more than a few players, switch to Paper (https://papermc.io) or Purpur (https://purpurmc.org). These are drop-in replacements for the vanilla server jar that include significant performance improvements:
- Asynchronous chunk loading
- Entity activation range limiting
- Configurable hopper and redstone behavior
- Tick skip optimizations for inactive chunks
- Anti-exploit patches
Download Paper and replace the vanilla jar:
shcd /home/minecraft/server fetch -o paper.jar "https://api.papermc.io/v2/projects/paper/versions/1.21.4/builds/latest/downloads/paper-1.21.4.jar" chown minecraft:minecraft paper.jar
Update the jar name in /etc/rc.conf:
shsysrc minecraft_jar="paper.jar"
Paper Configuration
Paper creates additional configuration files. The most impactful settings in config/paper-global.yml and config/paper-world-defaults.yml:
yaml# paper-world-defaults.yml - key performance settings chunks: max-auto-save-chunks-per-tick: 8 entity-per-chunk-save-limit: experience_orb: 16 arrow: 16 snowball: 8 environment: optimize-explosions: true entities: spawning: despawn-ranges: soft: 30 hard: 56 per-player-mob-spawns: true
FreeBSD-Specific Tuning
Add these to /etc/sysctl.conf for better network performance with many player connections:
sh# Increase socket buffer sizes kern.ipc.maxsockbuf=16777216 net.inet.tcp.sendbuf_max=16777216 net.inet.tcp.recvbuf_max=16777216 # Increase the maximum number of open files kern.maxfiles=65536 kern.maxfilesperproc=32768
Apply without rebooting:
shsysctl -f /etc/sysctl.conf
Step 11: Plugin Support with Paper
Paper is fully compatible with Bukkit and Spigot plugins, which gives you access to thousands of server plugins. Some essential plugins for any server:
- EssentialsX -- core server commands (homes, warps, kits, economy)
- WorldGuard -- region protection to prevent griefing
- LuckPerms -- permission management
- CoreProtect -- block logging and rollback (pairs excellently with ZFS snapshots for a belt-and-suspenders approach)
- Chunky -- pre-generate world chunks so players do not cause lag loading new terrain
Install plugins by placing .jar files in the plugins/ directory:
shmkdir -p /home/minecraft/server/plugins cd /home/minecraft/server/plugins # Example: download EssentialsX fetch https://github.com/EssentialsX/Essentials/releases/download/2.20.1/EssentialsX-2.20.1.jar chown -R minecraft:minecraft /home/minecraft/server/plugins
Restart the server to load new plugins:
shservice minecraft restart
Step 12: World Backup with ZFS Snapshots
This is where FreeBSD truly shines. ZFS snapshots are instant, atomic, and space-efficient. A snapshot of a 5 GB world takes milliseconds and initially consumes zero additional disk space -- it only grows as blocks change.
Setting Up the ZFS Dataset
If you have not already, create a dedicated dataset for the Minecraft world:
shzfs create -o mountpoint=/home/minecraft/server/world zpool/minecraft-world chown -R minecraft:minecraft /home/minecraft/server/world
Automated Backup Script
Create /usr/local/bin/minecraft-backup.sh:
sh#!/bin/sh # # minecraft-backup.sh - Automated ZFS snapshot backup for Minecraft worlds # # Creates timestamped snapshots, prunes old ones, and optionally sends # snapshots to a remote backup pool. # # Configuration DATASET="zpool/minecraft-world" SNAPSHOT_PREFIX="auto" KEEP_HOURLY=24 KEEP_DAILY=30 KEEP_WEEKLY=12 LOGFILE="/var/log/minecraft-backup.log" REMOTE_HOST="" # Set to "user@backup-host" for remote replication REMOTE_DATASET="" # Set to "backup-pool/minecraft" for remote replication # Timestamp NOW=$(date +%Y-%m-%d_%H-%M-%S) SNAP_NAME="${DATASET}@${SNAPSHOT_PREFIX}-${NOW}" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "${LOGFILE}" } # Verify the dataset exists if ! zfs list "${DATASET}" > /dev/null 2>&1; then log "ERROR: Dataset ${DATASET} does not exist." exit 1 fi # Issue a save-all to the server (flush world data to disk) # This requires RCON or a named pipe; skip if not available if command -v mcrcon > /dev/null 2>&1; then mcrcon -H 127.0.0.1 -P 25575 -p "yourpassword" "save-all flush" 2>/dev/null sleep 3 fi # Create the snapshot if zfs snapshot "${SNAP_NAME}"; then log "OK: Created snapshot ${SNAP_NAME}" else log "ERROR: Failed to create snapshot ${SNAP_NAME}" exit 1 fi # Optional: send to remote host if [ -n "${REMOTE_HOST}" ] && [ -n "${REMOTE_DATASET}" ]; then LAST_REMOTE=$(zfs list -t snapshot -o name -s creation "${DATASET}" 2>/dev/null | grep "${SNAPSHOT_PREFIX}" | tail -2 | head -1) if [ -n "${LAST_REMOTE}" ] && [ "${LAST_REMOTE}" != "${SNAP_NAME}" ]; then zfs send -i "${LAST_REMOTE}" "${SNAP_NAME}" | ssh "${REMOTE_HOST}" zfs recv "${REMOTE_DATASET}" 2>/dev/null if [ $? -eq 0 ]; then log "OK: Sent incremental snapshot to ${REMOTE_HOST}:${REMOTE_DATASET}" else log "WARN: Remote replication failed." fi fi fi # Prune old snapshots # Keep KEEP_HOURLY hourly snapshots, then thin to daily, then weekly SNAPSHOTS=$(zfs list -t snapshot -o name -s creation "${DATASET}" | grep "${SNAPSHOT_PREFIX}") TOTAL=$(echo "${SNAPSHOTS}" | wc -l | tr -d ' ') if [ "${TOTAL}" -gt "${KEEP_HOURLY}" ]; then PRUNE_COUNT=$((TOTAL - KEEP_HOURLY)) echo "${SNAPSHOTS}" | head -n "${PRUNE_COUNT}" | while read -r snap; do # Check age -- keep one per day for KEEP_DAILY days SNAP_DATE=$(echo "${snap}" | sed "s/.*@${SNAPSHOT_PREFIX}-//" | cut -d'_' -f1) SNAP_AGE_DAYS=$(( ($(date +%s) - $(date -j -f "%Y-%m-%d" "${SNAP_DATE}" +%s 2>/dev/null || echo 0)) / 86400 )) if [ "${SNAP_AGE_DAYS}" -lt "${KEEP_DAILY}" ]; then # Within daily retention -- keep only one per day DAY_COUNT=$(echo "${SNAPSHOTS}" | grep "${SNAP_DATE}" | wc -l | tr -d ' ') if [ "${DAY_COUNT}" -gt 1 ]; then zfs destroy "${snap}" && log "PRUNED: ${snap}" fi elif [ "${SNAP_AGE_DAYS}" -lt $((KEEP_WEEKLY * 7)) ]; then # Within weekly retention -- keep only one per week zfs destroy "${snap}" && log "PRUNED: ${snap}" else # Beyond all retention zfs destroy "${snap}" && log "PRUNED: ${snap}" fi done fi log "Backup cycle complete. Total snapshots: $(zfs list -t snapshot "${DATASET}" 2>/dev/null | grep "${SNAPSHOT_PREFIX}" | wc -l | tr -d ' ')"
Make it executable and schedule with cron:
shchmod 755 /usr/local/bin/minecraft-backup.sh
Add to root's crontab (crontab -e):
shell# Minecraft world backup - every hour 0 * * * * /usr/local/bin/minecraft-backup.sh
Manual Snapshot and Rollback
For quick manual operations:
sh# Take a snapshot before risky operations zfs snapshot zpool/minecraft-world@before-plugin-test # Something went wrong? Roll back instantly zfs rollback zpool/minecraft-world@before-plugin-test
This is invaluable when testing new plugins, updating the server jar, or recovering from griefing. The rollback is instant regardless of world size. See our ZFS guide for more on snapshot management and replication.
Step 13: Whitelist and Security
Enable the Whitelist
For a private server, the whitelist is your first line of defense:
properties# In server.properties white-list=true enforce-whitelist=true
Manage the whitelist from the server console or via RCON:
shellwhitelist add PlayerName whitelist remove PlayerName whitelist list
Keep online-mode Enabled
The online-mode=true setting verifies every connecting player against Mojang's authentication servers. This ensures that players are who they claim to be and prevents stolen or cracked accounts from joining. Never disable this on a public-facing server.
Operator Security
Limit who has operator access. Use the ops.json file and set sensible permission levels (1-4). Level 4 gives full server control, which should be reserved for the server owner only.
Step 14: PF Firewall Rules
Lock down the network with PF. The Minecraft server only needs port 25565/tcp open (and 25575/tcp if you use RCON, but only from trusted IPs).
Add to /etc/pf.conf:
shell# Minecraft server PF rules # Define interfaces and variables ext_if = "vtnet0" minecraft_port = "25565" rcon_port = "25575" admin_ips = "{ 203.0.113.10, 198.51.100.20 }" # Default deny block in all pass out all keep state # Allow SSH from anywhere (or restrict to admin_ips) pass in on $ext_if proto tcp to port 22 keep state # Allow Minecraft connections from anyone pass in on $ext_if proto tcp to port $minecraft_port keep state # Allow RCON only from trusted admin IPs pass in on $ext_if proto tcp from $admin_ips to port $rcon_port keep state # Optional: rate-limit new connections to prevent DoS pass in on $ext_if proto tcp to port $minecraft_port \ flags S/SA keep state \ (max-src-conn 3, max-src-conn-rate 5/30, overload <bruteforce> flush global) # Block overloaded IPs block in quick from <bruteforce>
Load the rules:
shpfctl -f /etc/pf.conf
Enable PF if it is not already running:
shsysrc pf_enable="YES" service pf start
The rate-limiting rule caps each source IP at 3 concurrent connections and 5 new connections per 30 seconds. This stops basic DDoS attempts and connection floods while allowing normal players through without issue.
Step 15: Monitoring with Screen or Tmux
The rc.d script logs output to /var/log/minecraft.log, which you can tail for monitoring. For interactive console access, you have a few options:
Option 1: Tail the Log
shservice minecraft console # or tail -f /var/log/minecraft.log
Option 2: Run in tmux for Interactive Console
If you need to type commands into the Minecraft console interactively, run the server inside a tmux session instead of using daemon:
sh# Start in a tmux session su -m minecraft -c "tmux new-session -d -s minecraft 'cd /home/minecraft/server && java -Xms4G -Xmx4G -jar paper.jar nogui'" # Attach to the console later tmux attach -t minecraft
Detach with Ctrl+B, then D. The server keeps running in the background.
Option 3: RCON
Enable RCON in server.properties for remote command execution:
propertiesenable-rcon=true rcon.password=a-long-random-password-here rcon.port=25575
Install an RCON client:
shpkg install mcrcon
Send commands remotely:
shmcrcon -H 127.0.0.1 -P 25575 -p "a-long-random-password-here" "list" mcrcon -H 127.0.0.1 -P 25575 -p "a-long-random-password-here" "say Server restarting in 5 minutes"
RCON is also useful for the backup script to issue save-all flush before taking a snapshot.
Frequently Asked Questions
How much RAM does a Minecraft server need on FreeBSD?
The JVM overhead on FreeBSD is comparable to Linux. For a vanilla server with 1-5 players, 2 GB of heap (-Xmx2G) is sufficient. For 10-20 players with plugins, allocate 4-6 GB. The operating system itself needs about 512 MB - 1 GB on top of what you give to Java, so plan your total system RAM accordingly.
Can I run Bedrock Edition on FreeBSD?
The official Bedrock Dedicated Server is a Windows/Linux binary with no FreeBSD port. You can run it under FreeBSD's Linux compatibility layer (linux64), but it is not officially supported and may have issues. For a native FreeBSD experience, Java Edition is the way to go. If you need to support Bedrock players connecting to a Java server, use the Geyser plugin with Paper.
Does the Linux compatibility layer affect Minecraft server performance?
If you are running the Java Edition server, you are running a native FreeBSD JVM (openjdk21 from packages). There is no Linux compatibility layer involved, and performance is native. The Linux compatibility layer only matters if you try to run Linux-native binaries, which is not necessary for Java Edition.
How do I update the Minecraft server?
Stop the server, take a ZFS snapshot (safety net), download the new server jar, and start the server again:
shservice minecraft stop zfs snapshot zpool/minecraft-world@pre-update cd /home/minecraft/server fetch -o server.jar "NEW_SERVER_URL_HERE" chown minecraft:minecraft server.jar service minecraft start
The server automatically upgrades world data on first start with a new version. If anything goes wrong, rollback the ZFS snapshot and re-download the old jar.
Can I run multiple Minecraft servers on one FreeBSD machine?
Yes. The cleanest approach is to run each server in its own jail with its own IP address, ZFS dataset, and resource limits. Create separate rc.d scripts (or parameterize the one above with different names) and assign different ports. This keeps the servers completely isolated from each other.
How do I migrate my existing Minecraft world to FreeBSD?
Copy your world/ directory (and world_nether/ and world_the_end/ if they exist) to the FreeBSD server. Use rsync or scp:
shrsync -avz /path/to/old/world/ minecraft@freebsd-server:/home/minecraft/server/world/
Make sure file ownership is correct (chown -R minecraft:minecraft world/) and start the server. Minecraft worlds are platform-independent -- the same world files work on Windows, Linux, and FreeBSD without modification.
Is there a web-based admin panel for FreeBSD?
Crafty Controller and AMP both run on FreeBSD (Crafty natively via Python, AMP via Mono/.NET). However, for a single server, the rc.d script plus RCON provides everything you need without the overhead of a web panel. Use a web panel only if you are managing multiple servers and need a GUI for non-technical administrators.
Summary
A Minecraft server on FreeBSD is not just a novelty -- it is a genuinely well-suited workload for the operating system. ZFS gives you instant, zero-downtime backups that would require complex scripting on any other platform. Jails provide real process isolation without the complexity of container orchestration. PF locks down network access in a readable configuration. And the whole thing runs as a proper rc.d service that starts on boot and stops cleanly on shutdown.
The investment in setting things up properly -- dedicated user, rc.d script, ZFS snapshots, PF rules, a jail if you want full isolation -- pays off every time you need to recover from a bad update, roll back after griefing, or sleep soundly knowing your server is not going to fall over because the OS decided to update itself at 2 AM.
For more on the technologies referenced in this guide, see our guides on FreeBSD jails, ZFS on FreeBSD, PF firewall configuration, and our roundup of the best games available on FreeBSD.