How to Deploy a Paper Minecraft Server on Ubuntu 20.04

Updated on 10 August, 2026
Deploy Paper Minecraft server on Ubuntu 20.04 using the Adoptium Temurin JDK, a checksum-verified PaperMC build, and a systemd service.
How to Deploy a Paper Minecraft Server on Ubuntu 20.04 header image

Paper is a high-performance Minecraft server implementation that extends Spigot with patches for improved tick performance, configurable gameplay mechanics, and an expanded plugin API. It remains compatible with Bukkit and Spigot plugins while correcting mechanics inconsistencies present in the vanilla server.

This article explains how to deploy Paper on an Ubuntu 20.04 server. It covers installing the Eclipse Temurin Java runtime from the Adoptium package repository, downloading a verified Paper build through the PaperMC Fill API, running the server under a dedicated system account managed by systemd, and opening the required firewall port.

Note
Ubuntu 20.04 LTS reached the end of its standard five-year support window on 31 May 2025. It no longer receives security updates through the normal channels, so a server running it accumulates unpatched kernel and package vulnerabilities and is not suitable for a host exposed to the public internet.

Prerequisites

Before you begin, you need to:

  • Have access to an Ubuntu 20.04 server instance as a non-root user with sudo privileges.
  • Allocate at least 4 GB of RAM to the instance. Paper reserves a fixed Java heap at startup, and a 4 GB instance supports a 2 GB heap.

Install the Java Runtime

Paper requires Java 25 or later starting with Minecraft 26.1. Eclipse Temurin provides supported OpenJDK builds through the Adoptium package repository at packages.adoptium.net.

  1. Update the package index.

    console
    $ sudo apt update
    
  2. Install the packages required to fetch the repository key and query the Paper API.

    console
    $ sudo apt install -y wget gnupg apt-transport-https jq curl
    
  3. Create the keyring directory.

    console
    $ sudo install -m 0755 -d /etc/apt/keyrings
    
  4. Download the Adoptium signing key and convert it to a binary keyring.

    console
    $ wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public \
        | gpg --dearmor \
        | sudo tee /etc/apt/keyrings/adoptium.gpg > /dev/null
    
  5. Grant read access to the keyring so that APT can verify the repository.

    console
    $ sudo chmod a+r /etc/apt/keyrings/adoptium.gpg
    
  6. Add the Adoptium repository and bind it to the keyring.

    console
    $ echo "deb [signed-by=/etc/apt/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print $2}' /etc/os-release) main" \
        | sudo tee /etc/apt/sources.list.d/adoptium.list
    

    The awk expression resolves the release codename from /etc/os-release, which returns focal on Ubuntu 20.04. The signed-by option scopes the key to this repository alone, unlike the deprecated apt-key add command, which trusted a key across every configured repository.

  7. Refresh the package index to include the new repository.

    console
    $ sudo apt update
    
  8. Install Temurin 25.

    console
    $ sudo apt install -y temurin-25-jdk
    
  9. Verify the installed runtime.

    console
    $ java -version
    

    The output confirms a Temurin LTS build:

    openjdk version "25.0.4" 2026-07-21 LTS
    OpenJDK Runtime Environment Temurin-25.0.4+7 (build 25.0.4+7-LTS)
    OpenJDK 64-Bit Server VM Temurin-25.0.4+7 (build 25.0.4+7-LTS, mixed mode, sharing)

Create a Service Account

Running a public-facing game server under a dedicated unprivileged account limits the damage a compromised plugin or exploit can cause. The account requires no login shell because systemd starts the process directly.

  1. Create a system user with a home directory and no interactive shell.

    console
    $ sudo useradd -r -m -U -d /opt/minecraft -s /usr/sbin/nologin minecraft
    
  2. Create the server directory and assign ownership to the new account.

    console
    $ sudo install -d -o minecraft -g minecraft /opt/minecraft/paper
    

Deploy Paper

PaperMC distributes builds through the Fill API at fill.papermc.io. The previous api.papermc.io/v2 endpoint now returns HTTP 410 Gone and cannot supply builds. Every Fill response includes a SHA-256 checksum, which lets you confirm the download before running it.

  1. Query the API for the current version identifier.

    console
    $ PAPER_VERSION=$(curl -s https://fill.papermc.io/v3/projects/paper \
        -H "accept: application/json" \
        | jq -r '.versions | to_entries | map(select(.key|test("^[0-9]"))) | .[0].value[0]')
    
  2. Display the resolved version.

    console
    $ echo "$PAPER_VERSION"
    

    The output shows the newest release, such as 26.2.

  3. Fetch the metadata for the latest build of that version.

    console
    $ BUILD_META=$(curl -s "https://fill.papermc.io/v3/projects/paper/versions/${PAPER_VERSION}/builds/latest" \
        -H "accept: application/json")
    
  4. Extract the download URL and its checksum.

    console
    $ PAPER_URL=$(echo "$BUILD_META" | jq -r '.downloads["server:default"].url')
    $ PAPER_SHA=$(echo "$BUILD_META" | jq -r '.downloads["server:default"].checksums.sha256')
    

    In the above command:

    • PAPER_URL: The signed object URL on fill-data.papermc.io that serves the server JAR.
    • PAPER_SHA: The SHA-256 digest published alongside the build.
  5. Download the server JAR.

    console
    $ sudo curl -fsSL -o /opt/minecraft/paper/paper.jar "$PAPER_URL"
    
  6. Verify the download against the published checksum.

    console
    $ echo "$PAPER_SHA  /opt/minecraft/paper/paper.jar" | sha256sum -c -
    

    The output reports OK. Stop and download the file again if the check reports a mismatch.

    /opt/minecraft/paper/paper.jar: OK
  7. Transfer ownership of the JAR to the service account.

    console
    $ sudo chown minecraft:minecraft /opt/minecraft/paper/paper.jar
    

Accept the End User License Agreement

Mojang requires explicit acceptance of the Minecraft End User License Agreement before a server accepts connections. The first launch writes a template eula.txt and exits without binding a port.

  1. Run the server once to generate the configuration files.

    console
    $ sudo -u minecraft java -Xms2G -Xmx2G -jar /opt/minecraft/paper/paper.jar --nogui
    

    The server writes its default files and exits with a notice:

    [21:47:15 WARN]: Failed to load eula.txt
    [21:47:15 INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
  2. Open the generated agreement file.

    console
    $ sudo -u minecraft nano /opt/minecraft/paper/eula.txt
    
  3. Change the eula value to true after reading the agreement.

    ini
    eula=true
    

    Save and close the file.

Run Paper as a systemd Service

A systemd unit starts Paper at boot, restarts it after a crash, and routes its console output to the journal. Stopping the unit sends SIGINT, which Paper traps to save every loaded world before exiting.

  1. Create the unit file.

    console
    $ sudo nano /etc/systemd/system/paper.service
    
  2. Add the following configuration:

    ini
    [Unit]
    Description=Paper Minecraft Server
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    User=minecraft
    Group=minecraft
    WorkingDirectory=/opt/minecraft/paper
    ExecStart=/usr/bin/java -Xms2G -Xmx2G -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 \
        -jar /opt/minecraft/paper/paper.jar --nogui
    KillSignal=SIGINT
    SuccessExitStatus=0 130 143
    Restart=on-failure
    RestartSec=10s
    PrivateTmp=true
    NoNewPrivileges=true
    ProtectSystem=full
    
    [Install]
    WantedBy=multi-user.target
    

    Save and close the file.

    • ExecStart: Launches Paper with the G1 garbage collector tuned for a fixed heap. Match -Xms and -Xmx to each other, and leave at least 2 GB of system memory outside the heap for the operating system and Java metadata.
    • KillSignal=SIGINT: Delivers the interrupt that Paper handles as a graceful shutdown, saving all dimensions before the process exits.
    • SuccessExitStatus=0 130 143: Treats the exit codes produced by SIGINT (130) and SIGTERM (143) as clean. Without 130, a normal systemctl stop leaves the unit in a failed state.
    • Restart=on-failure: Recovers the server after a crash, but leaves it stopped after a deliberate systemctl stop.
    • ProtectSystem=full: Mounts /usr and /boot read-only for the service, restricting writes to the server directory.
  3. Reload the systemd manager configuration.

    console
    $ sudo systemctl daemon-reload
    
  4. Enable the service to start at boot.

    console
    $ sudo systemctl enable paper
    
  5. Start the service.

    console
    $ sudo systemctl start paper
    
  6. Confirm the service is running.

    console
    $ sudo systemctl status paper
    

    Verify that the output reports Active: active (running).

  7. Follow the startup log until the server reports readiness.

    console
    $ sudo journalctl -u paper -f -o cat
    

    World generation on a new server takes roughly 20 to 45 seconds:

    [21:48:21 INFO]: Done (41.618s)! For help, type "help"

    Press Ctrl + C to stop following the log. The server keeps running.

Configure the Firewall

Paper listens on TCP port 25565. Ubuntu ships with ufw inactive, so enable it and permit both SSH and the game port before locking the host down.

  1. Allow SSH so that enabling the firewall does not end your session.

    console
    $ sudo ufw allow OpenSSH
    
  2. Allow Minecraft client traffic.

    console
    $ sudo ufw allow 25565/tcp
    
  3. Enable the firewall.

    console
    $ sudo ufw enable
    

    The command warns that it may disrupt existing SSH connections and asks for confirmation. Enter y to proceed. The preceding rule keeps your session open.

  4. Review the active rules.

    console
    $ sudo ufw status
    

    The output lists both services:

    Status: active
    
    To                         Action      From
    --                         ------      ----
    OpenSSH                    ALLOW       Anywhere
    25565/tcp                  ALLOW       Anywhere
    OpenSSH (v6)               ALLOW       Anywhere (v6)
    25565/tcp (v6)             ALLOW       Anywhere (v6)

Verify the Server

Confirm that Paper holds the port locally and answers connections from outside the instance.

  1. Verify that the Java process is listening.

    console
    $ sudo ss -tlnp | grep 25565
    

    The output shows the listening socket owned by java:

    LISTEN 0  4096  *:25565  *:*  users:(("java",pid=26823,fd=129))
  2. Test the port from your local machine, replacing SERVER-IP with your instance's public IP address.

    console
    $ nc -z -w6 SERVER-IP 25565
    
  3. Open the Minecraft Java Edition client, select Multiplayer, click Add Server, and enter your instance's public IP address in the Server Address field.

    Note
    The server binds port 25565 only after the log prints Done. A connection attempt during world generation fails even though the service is already active.

Manage the Server

The service exposes standard systemd controls, and the journal retains the full server console history.

  1. Stop the server, allowing it to save all worlds.

    console
    $ sudo systemctl stop paper
    
  2. Restart the server after changing a configuration file.

    console
    $ sudo systemctl restart paper
    
  3. Review recent console output.

    console
    $ sudo journalctl -u paper -n 100 -o cat
    

To issue in-game commands from a terminal, enable RCON by setting enable-rcon=true, rcon.password, and rcon.port in /opt/minecraft/paper/server.properties, then restart the service. Restrict the RCON port to trusted addresses in ufw, because the protocol transmits its password without encryption.

Conclusion

You have successfully deployed Paper on your Ubuntu 20.04 server using the supported Adoptium repository for Java, a checksum-verified build from the PaperMC Fill API, and a systemd service that runs the server under a dedicated account. Adjust gameplay settings in server.properties, tune Paper-specific behavior in config/paper-global.yml, and add plugins to the plugins directory before restarting the service. For more information, visit the official Paper documentation.

Comments