Cover image for Ubuntu 26.04 LTS Server Setup for Developers: What Changed and What to Watch

At a glance

Reading time

~200 words/min

Published

12 hours ago

Jun 4, 2026

Views

9

All-time total

Ubuntu 26.04 LTS Server Setup for Developers: What Changed and What to Watch

A new Ubuntu LTS lands every two years, and 26.04 is the one most teams will run in production through the rest of the decade. A fresh LTS is the right moment to revisit your server baseline not to chase novelty, but because the defaults, kernel, and toolchain have all moved, and a few of those moves will bite you if you provision a box the way you did in 2024. This guide is a developer-focused setup walkthrough: a secure, sensible baseline for 26.04, plus the specific changes worth watching before you migrate workloads onto it.

What you will walk away with

  • A hardened, repeatable baseline for a fresh 26.04 server
  • The defaults and toolchain shifts that differ from 24.04
  • SSH, firewall, and unattended-upgrades done correctly
  • Why LTS timing and the support window should drive your choice
  • A pre-migration checklist so nothing surprises you in production
i

Info

LTS is about the support window, not the version number

The reason to standardise on an LTS is years of security updates and predictable behaviour. 26.04 resets that clock picking it now means you are not forced into another OS migration for a long time. That stability is the whole point of server LTS.

Warning

Always confirm specifics against the official release notes

This guide covers the categories of change you should check. Exact version numbers, deprecations, and package availability shift right up to release, so verify the details in Ubuntu's official 26.04 release notes for your architecture before you commit a production migration.

First, decide if now is the moment

A brand-new LTS is rock-solid for fresh installs but worth a beat of caution for migrating existing fleets: third-party repositories, drivers, and niche packages sometimes lag a release by weeks. For a new project, install 26.04 today. For an existing 24.04 fleet running fine, plan the migration deliberately rather than rushing it on week one there is a companion comparison guide dedicated to exactly that upgrade decision.

The secure baseline, step by step

Whatever the version, a server's first hour should look the same: patch, create a non-root user, lock down SSH, enable the firewall, and automate security updates. Here is the modern baseline.

1

Update and create a sudo user

Never operate as root. Patch fully, then create your working account.

apt update && apt full-upgrade -y
adduser deploy
usermod -aG sudo deploy
# Copy your SSH key to the new user before you lock down SSH:
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
2

Harden SSH: keys only

Disable password and root login. Key-based auth is the single biggest win for server security.

# /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
# then: systemctl restart ssh
3

Enable the firewall

Default-deny inbound, allow only what you serve. Open SSH before you enable, or you will lock yourself out.

ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
4

Automate security updates

Unattended-upgrades applies security patches without you babysitting the box. On a server, this is not optional.

apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
# Confirm security origins are enabled in
# /etc/apt/apt.conf.d/50unattended-upgrades
💡

Pro tip

Bake this baseline into provisioning (cloud-init, Ansible, or your image builder) rather than running it by hand. A server you can recreate identically from code is a server you can patch, audit, and replace without fear and it makes the next LTS migration a config change, not a weekend.

In practice that means the whole baseline above collapses into a cloud-init file your provider runs on first boot the server comes up already hardened, with no manual SSH session at all:

#cloud-config
users:
  - name: deploy
    groups: [sudo]
    shell: /bin/bash
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... you@laptop
ssh_pwauth: false                 # keys only, from the very first boot
package_update: true
package_upgrade: true
packages: [ufw, unattended-upgrades, fail2ban]
runcmd:
  - ufw default deny incoming
  - ufw allow OpenSSH
  - ufw allow 80,443/tcp
  - ufw --force enable
  - systemctl enable --now fail2ban     # ban repeated SSH brute-force attempts

Run your app as a service behind nginx

A hardened box is only half the job your app needs to run as a supervised service that restarts on crash and on reboot, behind a reverse proxy that terminates TLS. On modern Ubuntu that is systemd plus nginx, and it is worth getting into your provisioning too.

# /etc/systemd/system/app.service
[Unit]
Description=My app
After=network.target

[Service]
User=deploy
WorkingDirectory=/srv/app
ExecStart=/srv/app/.venv/bin/gunicorn app:app --bind 127.0.0.1:8000 --workers 4
Restart=always            # crash? reboot? systemd brings it back
RestartSec=3

[Install]
WantedBy=multi-user.target
# enable with: sudo systemctl enable --now app
# /etc/nginx/sites-available/app  — reverse proxy, TLS terminated here.
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;     # to the systemd-managed app
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

What to watch on 26.04 specifically

Beyond the universal baseline, a new LTS shifts several things that touch developers directly. Check each of these against your stack before migrating.

A newer kernel and toolchain

An LTS ships a newer kernel (better hardware support and performance) and refreshed compilers and core libraries. Most software benefits, but anything pinned to an exact kernel module, a specific compiler version, or an older system library needs verification. Custom or out-of-tree kernel modules and proprietary drivers are the usual sticking points.

Updated language runtimes

The default system Python, and the packaged versions of other runtimes, advance with the release. Code or tooling relying on the old default can break on subtle version differences. The durable fix is to stop depending on the system runtime at all: pin your application's language version explicitly (containers, version managers, or vendored runtimes) so the OS upgrade cannot move it under you.

Packaging and default shifts

Ubuntu has been steadily moving packages between formats and changing defaults around things like network management and, increasingly, how some CLI tooling is delivered. The practical risk is a script that assumes a package is installed a particular way, or a default config file living where it used to. Audit your provisioning scripts for hardcoded paths and package assumptions.

💡

Tip

Containers absorb most of the churn

If your app runs in containers, the host OS upgrade mostly affects the kernel and the container runtime, not your application's dependencies. That isolation is exactly why containerised workloads ride LTS upgrades far more smoothly than apps installed directly on the host.

The pre-migration checklist

Pros

  • Test the full deploy on a 26.04 staging box before touching production
  • Confirm every third-party apt repo publishes for 26.04
  • Verify drivers/kernel modules (GPU, storage, networking) are supported
  • Pin app language runtimes so the OS default cannot shift them
  • Re-run your provisioning from scratch on a clean 26.04 image

Cons

  • No in-place production upgrade without a staging rehearsal
  • No assuming third-party repos already support the new release
  • No depending on the system Python/runtime for your app
  • No hardcoded package paths that a packaging change can break
LTS

years of predictable security updates — the real reason to standardise on it

The bottom line

Ubuntu 26.04 LTS is the sensible production baseline for new servers and a worthwhile, but deliberate, migration target for existing ones. The setup that matters most has not changed patch, non-root user, key-only SSH, default-deny firewall, automatic security updates, all driven from code. Layer on a staging rehearsal and a check of the release-specific kernel, runtime, and packaging shifts, and you will land on 26.04 with a server that is secure today and stays supported for years.

! Common mistakes to avoid

  • Operating day-to-day as the root user.

    Create a sudo user and disable root SSH login from the first boot.

  • Leaving password SSH authentication enabled.

    Switch to key-only auth — copy your key first, then set PasswordAuthentication no.

  • Enabling the firewall before allowing SSH.

    Always ufw allow OpenSSH before ufw enable, or you will lock yourself out.

  • Depending on the system Python for your app.

    Pin the app runtime above the OS (containers/version managers) so an upgrade can't move it.

? Frequently asked questions

Should I run 26.04 on a brand-new server? +

Yes. For a fresh install there is no reason to start on the older LTS — you get the newer kernel, toolchain, and the longest support window.

What is the most important first-hour setup? +

Patch fully, create a non-root sudo user, switch SSH to key-only and disable root login, enable a default-deny UFW firewall, and turn on unattended-upgrades for security patches.

Do I really need unattended-upgrades on a server? +

For security patches, effectively yes. It applies critical updates without you babysitting the box, closing the window between a vulnerability disclosure and your patch.

What changes should I watch on 26.04 specifically? +

The newer kernel (verify custom modules and proprietary drivers), updated default language runtimes, and packaging/default shifts that can trip provisioning scripts with hardcoded paths.

Why do containers make LTS upgrades easier? +

A containerised app depends on its own bundled runtime, so a host OS upgrade mostly affects the kernel and container runtime — not your application's dependencies.

Success

Reproducibility beats heroics

The teams who upgrade LTS painlessly are the ones whose servers are defined in code. Get your baseline into provisioning once, and every future migration including this one becomes boring. Boring is exactly what you want from infrastructure.

Newsletter

Want more posts like this?

Get practical software notes and tutorials delivered when something new is published.

No spam. Unsubscribe anytime.

Share

Related posts

How to create a new user & configure a firewall on Ubuntu 18.4

This tutorial will guide you to set up a new user and to configure the firewall with UFW on Ubuntu 18.04.

6 years ago

Installing Nginx, PHP, MySQL and PHPMyAdmin on Ubuntu 18.04

This tutorial is created to set up Nginx and PHPMyAdmin along with PHP 7.4 on ubuntu 18.04 with simple and easy steps.

6 years ago