阅读视图

发现新文章,点击刷新页面。

How to Install LEMP Stack on Ubuntu 26.04

When you need to host a PHP application or a CMS such as WordPress, Magento, or Laravel on a fresh Ubuntu server, the LEMP stack is one of the most common ways to get there. LEMP stands for Linux, Nginx (pronounced “engine-x”), MySQL, and PHP, and the four pieces fit together to serve dynamic PHP pages over HTTP.

This guide explains how to install and configure a complete LEMP stack on Ubuntu 26.04. By the end you will have Nginx serving HTTP traffic, MySQL 8.4 running as the database, and PHP 8.5 processing dynamic pages through PHP-FPM.

Quick Reference

Task Command
Update package index sudo apt update
Install Nginx sudo apt install nginx
Install MySQL sudo apt install mysql-server
Install PHP-FPM sudo apt install php-fpm php-mysql
Secure MySQL sudo mysql_secure_installation
Restart PHP-FPM sudo systemctl restart php8.5-fpm
Reload Nginx sudo systemctl reload nginx
Test Nginx config sudo nginx -t
Allow HTTP/HTTPS sudo ufw allow 'Nginx Full'
PHP-FPM socket /run/php/php8.5-fpm.sock

Prerequisites

Before installing the stack, make sure you have:

Step 1: Install Nginx

Nginx is in the default Ubuntu 26.04 repositories. Refresh the package index and install it:

Terminal
sudo apt update
sudo apt install nginx

Once the install finishes, the service starts automatically. Confirm it is running:

Terminal
sudo systemctl status nginx

The output shows the service as active (running). If UFW is enabled, allow HTTP and HTTPS traffic:

Terminal
sudo ufw allow 'Nginx Full'

Open http://your_server_ip in a browser. You should see the default Nginx welcome page, which confirms that Nginx is serving requests.

For a deeper walkthrough of the install and the directory layout, see How to Install Nginx on Ubuntu 26.04 .

Step 2: Install MySQL

The database tier handles persistence. Install the MySQL server package:

Terminal
sudo apt install mysql-server

Once the install completes, run the security script. It walks you through removing anonymous users, disabling remote root login, and dropping the test database:

Terminal
sudo mysql_secure_installation

When prompted, answer Y to each hardening question. The validate password component is optional and you can disable it if you plan to manage credentials yourself.

To verify that the service is running:

Terminal
sudo systemctl status mysql

For details on creating users and granting privileges, see How to Install MySQL on Ubuntu 26.04 .

Step 3: Install PHP and PHP-FPM

Nginx does not embed a PHP interpreter. Instead, requests for PHP files are handed off to PHP-FPM (FastCGI Process Manager) over a Unix socket. Install PHP-FPM and the MySQL driver:

Terminal
sudo apt update
sudo apt install php-fpm php-mysql

On Ubuntu 26.04 this pulls in PHP 8.5. Confirm the version:

Terminal
php -v

The output starts with PHP 8.5.x. The PHP-FPM service starts automatically and listens on a Unix socket at /run/php/php8.5-fpm.sock:

Terminal
sudo systemctl status php8.5-fpm

If you need extra extensions such as php-curl, php-gd, php-mbstring, php-xml, or php-zip, install them now:

Terminal
sudo apt install php-curl php-gd php-mbstring php-xml php-zip

For information on additional modules and switching PHP versions, see How to Install PHP on Ubuntu 26.04 .

Step 4: Configure Nginx to Serve PHP

Now that Nginx and PHP-FPM are running, you need a server block that passes .php requests to PHP-FPM. Create a directory for the site and a small test file:

Terminal
sudo mkdir -p /var/www/example.com
sudo chown -R $USER:$USER /var/www/example.com

Create a new server block configuration:

Terminal
sudo nano /etc/nginx/sites-available/example.com

Paste the following content. Replace example.com with your domain or your server IP if you do not have a domain yet:

nginx
server {
 listen 80;
 listen [::]:80;

 server_name example.com www.example.com;
 root /var/www/example.com;

 index index.php index.html;

 location / {
 try_files $uri $uri/ =404;
 }

 location ~ \.php$ {
 include snippets/fastcgi-php.conf;
 fastcgi_pass unix:/run/php/php8.5-fpm.sock;
 }

 location ~ /\.ht {
 deny all;
 }
}

The try_files directive serves static files when they exist and falls back to a 404 otherwise. The location ~ \.php$ block matches any request ending in .php and forwards it to PHP-FPM through the Unix socket. The final location ~ /\.ht block blocks access to legacy .htaccess files, which Nginx does not use.

Enable the site by creating a symlink in sites-enabled:

Terminal
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

If the default server block is still enabled, remove its symlink so Nginx serves your new site for matching requests:

Terminal
sudo unlink /etc/nginx/sites-enabled/default

Test the configuration before reloading:

Terminal
sudo nginx -t

The output should end with syntax is ok and test is successful. Reload Nginx to apply the change:

Terminal
sudo systemctl reload nginx

Step 5: Test the PHP Setup

Create a simple PHP info file to confirm that Nginx hands PHP requests to PHP-FPM:

Terminal
echo "<?php phpinfo();" | sudo tee /var/www/example.com/info.php

Open http://example.com/info.php (or http://your_server_ip/info.php) in a browser. You should see the PHP information page that lists the PHP version, loaded extensions, and configuration directives. If the page shows the source code as plain text, Nginx is not passing the request to PHP-FPM and the location ~ \.php$ block needs to be reviewed.

Once you confirm PHP is working, remove the info file. It exposes details about your environment that should not be publicly readable:

Terminal
sudo rm /var/www/example.com/info.php

Step 6: Test the MySQL Connection from PHP

To confirm that PHP can talk to MySQL, create a test database and user:

Terminal
sudo mysql

In the MySQL prompt, run:

sql
CREATE DATABASE lemp_test;
CREATE USER 'lemp_user'@'localhost' IDENTIFIED BY 'changeme';
GRANT ALL PRIVILEGES ON lemp_test.* TO 'lemp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Warning
The password above is a placeholder. Replace changeme with a strong value, and do not commit credentials to version control. Store secrets in environment variables or a .env file outside the repository.

Create a small connection test:

Terminal
sudo nano /var/www/example.com/db-test.php

Add the following content:

php
<?php
$mysqli = new mysqli("localhost", "lemp_user", "changeme", "lemp_test");
if ($mysqli->connect_error) {
 die("Connection failed: " . $mysqli->connect_error);
}
echo "Connected to MySQL " . $mysqli->server_info;
$mysqli->close();

Open http://example.com/db-test.php. The page prints Connected to MySQL 8.4.x when the credentials and the PHP MySQL driver are working. Remove the file once you confirm the connection:

Terminal
sudo rm /var/www/example.com/db-test.php

Troubleshooting

Nginx shows the welcome page after editing the server block
Nginx still serves the default site. Remove the symlink at /etc/nginx/sites-enabled/default or rename your server block so it loads first, then run sudo nginx -t and sudo systemctl reload nginx.

PHP files download instead of executing
Nginx is not forwarding .php requests to PHP-FPM. Verify that the location ~ \.php$ block exists, that fastcgi_pass points to /run/php/php8.5-fpm.sock, and that PHP-FPM is running with sudo systemctl status php8.5-fpm.

502 Bad Gateway
PHP-FPM is not running, or Nginx is pointing at a wrong socket path. Run ls -l /run/php/ and confirm that the socket file matches the version in your config (for example, php8.5-fpm.sock). Restart the service with sudo systemctl restart php8.5-fpm.

Permission denied accessing the socket
The socket is owned by www-data by default. Make sure Nginx runs as www-data (the default on Ubuntu) and that no custom file permissions on /run/php/ block access.

MySQL connection refused from PHP
Confirm that the php-mysql package is installed, that the user has been granted privileges on the target database, and that the password in the PHP code matches the one used in CREATE USER.

FAQ

What is the difference between LEMP and LAMP?
LEMP uses Nginx as the web server, while LAMP uses Apache. Nginx handles many concurrent connections with low memory and is a strong fit for static content and reverse proxying. Apache supports per-directory .htaccess files and mod_php, which simplifies shared hosting setups. For a step-by-step Apache install, see How to Install LAMP Stack on Ubuntu 26.04 .

Can I use MariaDB instead of MySQL?
Yes. MariaDB is a drop-in replacement for MySQL and uses the same client tools, the same protocol, and the same SQL syntax for the cases covered in this guide.

Which PHP-FPM socket path do I use?
The path follows the PHP version. On Ubuntu 26.04 with the default PHP 8.5, the socket is /run/php/php8.5-fpm.sock. If you install another version, list /run/php/ to find the right socket file.

How do I add HTTPS to the site?
The recommended path is to install Certbot and request a Let’s Encrypt certificate for your domain. The Certbot Nginx plugin updates the server block automatically and sets up redirection from HTTP to HTTPS.

Next Steps

You now have a working LEMP stack on Ubuntu 26.04. From here you can deploy a PHP application, create additional Nginx server blocks for more sites, or front the stack with HTTPS using Let’s Encrypt.

How to Install LAMP Stack on Ubuntu 26.04

When you spin up a new server to run a PHP application or a CMS such as WordPress, Joomla, or Drupal, the LAMP stack is one of the most familiar starting points. LAMP stands for Linux, Apache, MySQL, and PHP, and the four pieces work together to serve dynamic web pages over HTTP.

This guide explains how to install and configure a complete LAMP stack on Ubuntu 26.04. By the end you will have Apache serving HTTP traffic, MySQL 8.4 running as the database, and PHP 8.5 processing dynamic pages through mod_php.

Quick Reference

Task Command
Update package index sudo apt update
Install Apache sudo apt install apache2
Install MySQL sudo apt install mysql-server
Install PHP with Apache sudo apt install php libapache2-mod-php php-mysql
Secure MySQL sudo mysql_secure_installation
Restart Apache sudo systemctl restart apache2
Test Apache config sudo apache2ctl configtest
Allow HTTP/HTTPS sudo ufw allow 'Apache Full'
Enable a site sudo a2ensite example.com
Disable default site sudo a2dissite 000-default

Prerequisites

Before installing the stack, make sure you have:

Step 1: Install Apache

Apache is in the default Ubuntu 26.04 repositories. Refresh the package index and install it:

Terminal
sudo apt update
sudo apt install apache2

The service starts automatically after the install. Confirm it is running:

Terminal
sudo systemctl status apache2

The output shows the service as active (running). If UFW is enabled, allow HTTP and HTTPS through the firewall:

Terminal
sudo ufw allow 'Apache Full'

Open http://your_server_ip in a browser. You should see the default Apache welcome page, which confirms that Apache is serving requests.

For an in-depth walkthrough of the install and the directory layout, see How to Install Apache on Ubuntu 26.04 .

Step 2: Install MySQL

The database tier handles persistence. Install the MySQL server package:

Terminal
sudo apt install mysql-server

Once the install completes, run the security script. It walks you through removing anonymous users, disabling remote root login, and dropping the test database:

Terminal
sudo mysql_secure_installation

Answer Y to each hardening question. The validate password component is optional; skip it if you plan to manage credentials yourself.

To verify that the service is running:

Terminal
sudo systemctl status mysql

For details on creating users and granting privileges, see How to Install MySQL on Ubuntu 26.04 .

Step 3: Install PHP

Apache integrates with PHP through the libapache2-mod-php module, which lets Apache run PHP code in-process. Install PHP, the Apache module, and the MySQL driver:

Terminal
sudo apt update
sudo apt install php libapache2-mod-php php-mysql

On Ubuntu 26.04 this pulls in PHP 8.5. Confirm the version:

Terminal
php -v

The output starts with PHP 8.5.x. Restart Apache so the PHP module loads:

Terminal
sudo systemctl restart apache2

If you need extra extensions such as php-curl, php-gd, php-mbstring, php-xml, or php-zip, install them now:

Terminal
sudo apt install php-curl php-gd php-mbstring php-xml php-zip

For information on additional modules and switching PHP versions, see How to Install PHP on Ubuntu 26.04 .

Step 4: Configure an Apache Virtual Host

The default Apache configuration serves files from /var/www/html. For a real site you usually want a dedicated document root and a virtual host. Create the directory and a small placeholder:

Terminal
sudo mkdir -p /var/www/example.com
sudo chown -R $USER:$USER /var/www/example.com
echo "<h1>example.com is live</h1>" > /var/www/example.com/index.html

Create a new virtual host file:

Terminal
sudo nano /etc/apache2/sites-available/example.com.conf

Paste the following content. Replace example.com with your domain or your server IP if you do not have a domain yet:

apache
<VirtualHost *:80>
 ServerName example.com
 ServerAlias www.example.com
 ServerAdmin webmaster@example.com
 DocumentRoot /var/www/example.com

 <Directory /var/www/example.com>
 Options -Indexes +FollowSymLinks
 AllowOverride All
 Require all granted
 </Directory>

 ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
 CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>

AllowOverride All lets you use per-directory .htaccess files, which is required by many PHP applications such as WordPress for clean URL rewrites. Enable the site and disable the default one:

Terminal
sudo a2ensite example.com
sudo a2dissite 000-default

Make sure mod_rewrite is enabled if your application uses it:

Terminal
sudo a2enmod rewrite

Test the configuration before reloading Apache:

Terminal
sudo apache2ctl configtest

The output should end with Syntax OK. Reload Apache to apply the change:

Terminal
sudo systemctl reload apache2

Step 5: Test the PHP Setup

Create a PHP info file to confirm that Apache executes PHP:

Terminal
echo "<?php phpinfo();" | sudo tee /var/www/example.com/info.php

Open http://example.com/info.php (or http://your_server_ip/info.php) in a browser. You should see the PHP information page that lists the PHP version, loaded extensions, and configuration directives. If the browser downloads the file or shows the source code, the PHP module is not loaded; reinstall libapache2-mod-php and restart Apache.

Once you confirm PHP is working, remove the info file. It exposes details about your environment that should not be publicly readable:

Terminal
sudo rm /var/www/example.com/info.php

Step 6: Test the MySQL Connection from PHP

To confirm that PHP can talk to MySQL, create a test database and user:

Terminal
sudo mysql

In the MySQL prompt, run:

sql
CREATE DATABASE lamp_test;
CREATE USER 'lamp_user'@'localhost' IDENTIFIED BY 'changeme';
GRANT ALL PRIVILEGES ON lamp_test.* TO 'lamp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Warning
The password above is a placeholder. Replace changeme with a strong value, and do not commit credentials to version control. Store secrets in environment variables or a .env file outside the repository.

Create a small connection test:

Terminal
sudo nano /var/www/example.com/db-test.php

Add the following content:

php
<?php
$mysqli = new mysqli("localhost", "lamp_user", "changeme", "lamp_test");
if ($mysqli->connect_error) {
 die("Connection failed: " . $mysqli->connect_error);
}
echo "Connected to MySQL " . $mysqli->server_info;
$mysqli->close();

Open http://example.com/db-test.php. The page prints Connected to MySQL 8.4.x when the credentials and the PHP MySQL driver are working. Remove the file once you confirm the connection:

Terminal
sudo rm /var/www/example.com/db-test.php

Troubleshooting

Apache shows the default page after editing the virtual host
The default site is still enabled. Run sudo a2dissite 000-default, then sudo a2ensite example.com, and reload Apache.

PHP files download instead of executing
The Apache PHP module is not loaded. Reinstall it with sudo apt install --reinstall libapache2-mod-php, enable it with sudo a2enmod php8.5, and restart Apache.

AllowOverride All is ignored
The <Directory> block in the virtual host must include AllowOverride All. Confirm that the path matches your DocumentRoot and run sudo apache2ctl configtest after the change.

.htaccess rewrites do not work
The rewrite module is not enabled. Run sudo a2enmod rewrite and restart Apache. Verify that the <Directory> block sets AllowOverride All.

MySQL connection refused from PHP
Confirm that the php-mysql package is installed, that the user has been granted privileges on the target database, and that the password in the PHP code matches the one used in CREATE USER.

FAQ

What is the difference between LAMP and LEMP?
LAMP uses Apache as the web server, while LEMP uses Nginx. Apache supports .htaccess overrides and mod_php, which makes shared hosting and many PHP CMS workflows simpler. Nginx is event-driven and tends to use less memory under high concurrency. For an Nginx-based stack, see How to Install LEMP Stack on Ubuntu 26.04 .

Can I use MariaDB instead of MySQL?
Yes. MariaDB is a drop-in replacement for MySQL and uses the same client tools, the same protocol, and the same SQL syntax for the cases covered in this guide.

Should I use mod_php or PHP-FPM with Apache?
mod_php is the simplest setup and is fine for many sites. For higher concurrency or when you run multiple PHP versions, switch to PHP-FPM with mpm_event and proxy_fcgi. The php-fpm package and the proxy_fcgi Apache module handle the connection.

How do I add HTTPS to the site?
Install Certbot and request a Let’s Encrypt certificate for your domain. The Certbot Apache plugin updates the virtual host automatically and sets up redirection from HTTP to HTTPS.

Next Steps

You now have a working LAMP stack on Ubuntu 26.04. From here you can deploy a PHP application, create additional Apache virtual hosts for more sites, or add HTTPS using Let’s Encrypt.

Nginx Location Blocks: Match Rules and Priority

Sooner or later, every Nginx configuration grows beyond a single catch-all rule. You want PHP files to reach PHP-FPM, static assets to be served directly with long cache headers, one endpoint to be proxied to an application server, and an admin path to be protected with basic auth. Each of these rules lives in its own location block.

The part that trips people up is not writing a location block, it is predicting which one Nginx will pick when a request could match more than one. Nginx does not scan top to bottom. It uses a specific priority order that mixes prefix length, match type, and regex ordering. This guide walks through the match types, the exact order Nginx follows, and the patterns you will use most often.

Location Block Syntax

A location block lives inside a server block and defines how Nginx handles requests whose URI matches a given pattern:

txt
location [modifier] pattern {
 # directives
}

The modifier is optional. When it is omitted, Nginx treats the pattern as a prefix. The pattern is a string (for prefix and exact matches) or a regular expression (when the modifier is ~ or ~*).

A minimal server block that uses two location blocks looks like this:

nginx
server {
 listen 80;
 server_name example.com;
 root /var/www/example.com;

 location / {
 try_files $uri $uri/ =404;
 }

 location /api/ {
 proxy_pass http://127.0.0.1:3000;
 }
}

Requests for /about.html fall into the first block and are served as static files. Requests for /api/users fall into the second block and are proxied to the application.

The Five Match Types

Nginx supports five kinds of location matches. Each one uses a different modifier, and each one has its own role in the priority order covered in the next section.

  • location /path/ - Prefix match. Matches any URI that begins with the given string. This is the default when no modifier is used.
  • location = /path - Exact match. Matches only when the request URI is exactly equal to the given string.
  • location ^~ /path/ - Preferential prefix match. Same matching rules as a plain prefix match, but tells Nginx to stop searching for regex matches once this block is selected as the longest prefix.
  • location ~ pattern - Case-sensitive regex match. Matches if the URI matches the regular expression.
  • location ~* pattern - Case-insensitive regex match. Same as ~, but ignores letter case.

There is also a sixth form, location @name, for named locations. Named locations are not used during the normal match process; they are jumped to from directives such as try_files and error_page. We cover them later in this guide.

How Nginx Picks a Location

When a request comes in, Nginx does not walk through location blocks top to bottom. It picks the block that wins according to a fixed priority order.

The order Nginx follows is:

  1. Look for an exact match (=). If one matches, stop and use it.
  2. Look at all prefix matches (plain and ^~) and remember the longest one that matches.
  3. If the longest prefix match was defined with ^~, stop and use it.
  4. Otherwise, go through regex matches (~ and ~*) in the order they appear in the configuration. The first one that matches wins.
  5. If no regex matches, fall back to the longest prefix match remembered in step 2.

The consequences of this order are worth pausing on. Regex blocks are checked in source order, but prefix blocks are not; the longest match wins regardless of position in the file. The ^~ modifier changes the outcome by skipping the regex pass entirely when it is the longest prefix match, even if a regex block would also have matched.

A Worked Example

The easiest way to understand the priority order is to trace a few requests through a real configuration:

nginx
server {
 listen 80;
 server_name example.com;
 root /var/www/example.com;

 location = / {
 return 200 "home\n";
 }

 location / {
 try_files $uri $uri/ =404;
 }

 location /images/ {
 expires 30d;
 }

 location ^~ /downloads/ {
 autoindex on;
 }

 location ~* \.(png|jpg|jpeg|gif)$ {
 expires 7d;
 access_log off;
 }

 location ~ \.php$ {
 fastcgi_pass unix:/run/php/php-fpm.sock;
 include fastcgi_params;
 }
}

Now trace what happens for a few requests:

  • GET /: The = block matches exactly and wins immediately. Nginx returns the text home.
  • GET /about.html: No exact match, no ^~ match, and no regex match. The longest prefix match is /, so the try_files block serves the static file.
  • GET /images/logo.png: The longest prefix match is /images/, which is a plain prefix (no ^~), so regex checking still happens. The first regex to match is the image extension block (~*), so that block wins and the file is served with a 7-day expiry.
  • GET /downloads/setup.exe: The longest prefix match is /downloads/, defined with ^~. Because of the ^~, regex checking is skipped and the directory listing block wins.
  • GET /info.php: The longest prefix match is /. Regex checking runs, and the \.php$ block matches, so the request is passed to PHP-FPM.

Notice that the block order in the file did not change the outcome for prefix matches. It only changed the outcome for regex matches, where the first match wins.

Named Locations

A named location starts with @ and does not take part in the matching logic. You can only enter a named location by being redirected there from another directive:

nginx
server {
 listen 80;
 server_name example.com;
 root /var/www/example.com;

 location / {
 try_files $uri $uri/ @fallback;
 }

 location @fallback {
 proxy_pass http://127.0.0.1:3000;
 }
}

In this configuration, Nginx first tries to serve the request as a static file. If the file is not found, try_files jumps to @fallback, which proxies the request to an application server on port 3000. This is a common pattern for single-page apps and for frameworks that handle their own routing.

Named locations are also useful with the error_page directive, where you can route errors into a named block that returns a custom response.

Common Patterns

Most Nginx configurations end up using a handful of recurring location patterns. The snippets below show the shapes you will reach for most often.

Serve Static Files with a Fallback

nginx
location / {
 try_files $uri $uri/ /index.html;
}

Nginx tries the requested URI as a file, then as a directory, and falls back to /index.html. This works well for single-page apps where the router lives in the browser.

Cache Long-Lived Assets

nginx
location ~* \.(?:css|js|woff2?|png|jpg|jpeg|gif|svg|ico)$ {
 expires 30d;
 add_header Cache-Control "public, immutable";
 access_log off;
}

Assets whose content is fingerprinted by the build pipeline can be cached aggressively. The access_log off directive keeps the access log focused on real page requests; see the Nginx log files guide for where those logs live and how to tune them.

Pass PHP Requests to PHP-FPM

nginx
location ~ \.php$ {
 include snippets/fastcgi-php.conf;
 fastcgi_pass unix:/run/php/php-fpm.sock;
}

The regex matches any URI that ends with .php. The fastcgi-php.conf snippet shipped with most distributions sets the correct SCRIPT_FILENAME and related parameters.

Proxy a Path to an Application

nginx
location /api/ {
 proxy_pass http://127.0.0.1:3000;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header X-Forwarded-Proto $scheme;
}

For a deeper walkthrough of the proxy headers and common pitfalls, see the Nginx reverse proxy guide .

Protect an Admin Path

nginx
location ^~ /admin/ {
 auth_basic "Restricted";
 auth_basic_user_file /etc/nginx/.htpasswd;
}

The ^~ modifier makes sure this block wins over any regex block (for example, a generic static-asset rule), so the auth challenge is not accidentally bypassed by a more specific pattern. Basic auth sends credentials in clear text, so serve the site over HTTPS; see how to redirect HTTP to HTTPS in Nginx .

Test the Configuration

After changing location blocks, always test the Nginx configuration before reloading the service:

Terminal
sudo nginx -t

If the syntax check passes, reload Nginx so the change takes effect without dropping active connections:

Terminal
sudo systemctl reload nginx

If the test fails, Nginx prints the file name and line number that caused the error. Fix that issue first, then run sudo nginx -t again.

Quick Reference

Modifier Meaning Priority
= Exact match 1 (highest)
^~ Preferential prefix match 2 (skips regex)
~ Case-sensitive regex 3 (in source order)
~* Case-insensitive regex 3 (in source order)
(none) Prefix match 4 (longest wins)
@name Named location Only reachable from directives

Common Mistakes

A few patterns cause most of the confusion around location blocks.

Assuming top-to-bottom evaluation. Moving a prefix block higher in the file does not make it more likely to match. Prefix matches are chosen by length, not by position.

Forgetting that ^~ skips regex. If a request matches both a ^~ prefix and a regex block, the regex block is not evaluated. This is a feature, not a bug, but it is easy to forget when debugging why a regex rule is being ignored.

Trailing slashes. location /images/ and location /images behave differently. The first only matches URIs that start with /images/, while the second also matches /imagesfoo. Stick to trailing slashes for directory-like prefixes.

Using proxy_pass with a trailing slash on the upstream. Writing proxy_pass http://backend/; rewrites the path in a different way than proxy_pass http://backend;. The Nginx proxy_pass documentation explains the URI replacement rules in detail.

Regex order. When two regex blocks can match the same URI, the one that appears first in the config wins. If requests are landing in the wrong regex block, check the order.

Conclusion

Most Nginx misconfigurations come from assumptions about the order of evaluation, not from the directives themselves. Once you know that exact matches win first, that ^~ can short-circuit the regex pass, and that regex blocks are tried in source order, the behavior of a complex server block becomes predictable.

dd Command in Linux: Copy Disks, Partitions, and Files

Most of the time when you copy a file on Linux, you reach for cp. It walks the filesystem, respects permissions, and works on individual files. But when you need to copy an entire disk, write an ISO image to a USB stick, or create a byte-for-byte backup of a partition, the filesystem view is not enough. For that, Linux ships with dd, a tool that copies raw data block by block between any two files or devices.

This guide explains how to use dd safely to write ISO images, clone disks, back up partitions, create test files, and benchmark storage performance.

dd Syntax

The general form of the dd command is:

txt
dd if=INPUT of=OUTPUT [OPTIONS]

The two key operands are if (input file) and of (output file). Either one can be a regular file, a block device such as /dev/sda, or even /dev/zero and /dev/urandom. Options control the block size, how many blocks to copy, and what conversions to apply.

Warning
dd writes exactly what you tell it to write, to exactly the target you point it at. Pointing of= at the wrong disk will overwrite that disk without warning or confirmation. Always double-check the target device with lsblk or sudo fdisk -l before running a dd command.

Writing an ISO Image to a USB Drive

The most common use for dd is flashing a Linux ISO to a USB drive so you can boot and install from it. First, identify the USB device with lsblk or sudo fdisk -l:

Terminal
lsblk
output
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 465.8G 0 disk
├─sda1 8:1 0 512M 0 part /boot/efi
└─sda2 8:2 0 465.3G 0 part /
sdb 8:16 1 14.5G 0 disk
└─sdb1 8:17 1 14.5G 0 part

Here sda is the system disk and sdb is the USB drive. Make sure the USB is unmounted before writing to it:

Terminal
sudo umount /dev/sdb1

Then write the ISO image:

Terminal
sudo dd if=ubuntu-24.04-desktop-amd64.iso of=/dev/sdb bs=4M status=progress oflag=sync

Notice that the output target is the whole disk (/dev/sdb), not a partition (/dev/sdb1). Writing an ISO to a partition would leave the USB unbootable. The options do the following:

  • bs=4M - Read and write 4 MB at a time, which is much faster than the 512-byte default.
  • status=progress - Print a live progress indicator so you can see how much has been written.
  • oflag=sync - Flush every write to the device, so the final byte count reflects data actually on disk.

When dd finishes, run sync once more to make sure all buffered writes hit the USB stick before you remove it:

Terminal
sync

For alternative methods and distro-specific tips, see the guide on how to create a bootable Linux USB drive .

Cloning a Whole Disk

To make an exact block-level copy of one disk onto another, point if= and of= at two different block devices:

Terminal
sudo dd if=/dev/sda of=/dev/sdb bs=64K conv=noerror,sync status=progress

The destination disk must be at least as large as the source. Anything already on it is overwritten. The conversion flags handle read errors gracefully:

  • conv=noerror - Keep going when a read error occurs instead of aborting.
  • conv=sync - Pad short reads with zeros so the output stays aligned with the source.

Clone offline whenever possible. Cloning a disk that is actively being written to produces an inconsistent copy, especially for databases and journaling filesystems.

Backing Up a Partition to an Image File

dd can also write a partition or whole disk to a regular file. That file becomes a bit-for-bit image you can restore later:

Terminal
sudo dd if=/dev/sda1 of=/backup/sda1.img bs=4M status=progress

To save space, pipe the output through gzip:

Terminal
sudo dd if=/dev/sda1 bs=4M status=progress | gzip -c > /backup/sda1.img.gz

The compressed image is much smaller for filesystems that contain text or empty space, though the compression step adds CPU time.

To restore the image to a partition:

Terminal
gunzip -c /backup/sda1.img.gz | sudo dd of=/dev/sda1 bs=4M status=progress

The target partition must be unmounted during restore, and it must be at least as large as the original.

Backing Up and Restoring the MBR

The Master Boot Record sits in the first 512 bytes of a disk that uses a traditional BIOS partition table. You can back it up with dd:

Terminal
sudo dd if=/dev/sda of=/backup/mbr.img bs=512 count=1

To restore the MBR, swap the input and output:

Terminal
sudo dd if=/backup/mbr.img of=/dev/sda bs=512 count=1

On modern UEFI systems the partition table uses GPT instead of MBR, so this trick only applies to older BIOS installations. For GPT disks, use sgdisk --backup and sgdisk --load-backup from the gdisk package.

Creating an Empty File of a Specific Size

dd is often used to create test files of a known size. The count option sets how many blocks to write, and bs sets the block size:

Terminal
dd if=/dev/zero of=testfile bs=1M count=100

This creates a 100 MB file filled with zeros, useful for testing backups, simulating quota limits, or preparing a swap file. To create a file filled with random data instead, read from /dev/urandom:

Terminal
dd if=/dev/urandom of=random.bin bs=1M count=10

Random data is slower to generate because the kernel has to produce the bytes, while /dev/zero is effectively free.

Creating a Swap File

One practical use of the empty-file pattern is creating a swap file :

Terminal
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

The chmod 600 step keeps the swap file readable only by root, which is required by mkswap on most distros. After swapon, the 2 GB swap file is active immediately. Add an entry to /etc/fstab to make it permanent.

Wiping a Disk

To destroy data on a disk before disposing of it or repurposing it, overwrite the entire device with zeros:

Terminal
sudo dd if=/dev/zero of=/dev/sdb bs=4M status=progress

For a stronger wipe that makes recovery harder on traditional spinning disks, use random data:

Terminal
sudo dd if=/dev/urandom of=/dev/sdb bs=4M status=progress

On SSDs, a dd wipe is not the right tool. SSDs remap blocks internally, so a full write does not guarantee every cell was overwritten. Use blkdiscard or the drive vendor’s secure-erase utility instead.

Benchmarking Disk Write Speed

dd is a quick way to measure sustained write throughput. Write a 1 GB file of zeros and let dd report the rate:

Terminal
dd if=/dev/zero of=tempfile bs=1M count=1024 oflag=direct
output
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 3.1 s, 346 MB/s

The oflag=direct flag bypasses the page cache, so the number reflects actual disk speed rather than memory throughput. For a read benchmark, drop the caches first and then read the file back:

Terminal
sudo sh -c "sync && echo 3 > /proc/sys/vm/drop_caches"
dd if=tempfile of=/dev/null bs=1M

Remove the test file when you are done:

Terminal
rm tempfile

For more detailed I/O profiling, tools like fio and ioping produce more accurate, repeatable results.

Showing Progress on a Running dd

If you started dd without status=progress, you can still ask it to print a status line by sending the USR1 signal to its process. From another terminal, run:

Terminal
sudo kill -USR1 $(pidof dd)

dd will respond by printing the number of bytes copied so far and its current rate, then keep running.

Common Options

A quick rundown of the options covered in this guide, plus a few more worth knowing:

  • if=FILE - Input file or device.
  • of=FILE - Output file or device.
  • bs=N - Block size for both reads and writes (e.g., 4M for 4 MB).
  • ibs=N / obs=N - Separate input and output block sizes.
  • count=N - Number of input blocks to copy.
  • skip=N - Skip N blocks at the start of the input.
  • seek=N - Skip N blocks at the start of the output.
  • status=progress - Print progress while copying.
  • conv=noerror - Continue past read errors.
  • conv=sync - Pad short reads with zeros to keep block alignment.
  • oflag=direct - Bypass the kernel page cache on write.
  • oflag=sync - Flush each write to the device before continuing.

Quick Reference

Command Description
sudo dd if=image.iso of=/dev/sdX bs=4M status=progress oflag=sync Write an ISO image to a USB drive
sudo dd if=/dev/sda of=/dev/sdb bs=64K conv=noerror,sync status=progress Clone one disk to another
sudo dd if=/dev/sda1 of=/backup/sda1.img bs=4M status=progress Back up a partition to an image file
sudo dd if=/dev/sda bs=4M status=progress | gzip -c > disk.img.gz Compressed disk image backup
sudo dd if=/dev/sda of=/backup/mbr.img bs=512 count=1 Back up the MBR
dd if=/dev/zero of=testfile bs=1M count=100 Create a 100 MB file of zeros
sudo dd if=/dev/zero of=/dev/sdX bs=4M status=progress Wipe a disk with zeros
dd if=/dev/zero of=tempfile bs=1M count=1024 oflag=direct Benchmark disk write speed
sudo kill -USR1 $(pidof dd) Ask a running dd for its progress

Troubleshooting

dd: failed to open ‘/dev/sdX’: Permission denied
Writing to a block device requires root privileges. Prefix the command with sudo. If you are already using sudo, double-check that the device path is correct and that the device is not exclusively held by another process.

dd: error writing ‘/dev/sdX’: No space left on device
The destination is smaller than the source. When cloning, the target disk or partition must be at least as large as the source. When writing an ISO, the USB drive must be larger than the ISO file.

The write is much slower than expected
The default block size of 512 bytes forces millions of tiny syscalls. Set bs=4M or bs=64K to speed things up. Also check whether other processes are doing heavy I/O on the same device.

The USB does not boot after writing the ISO
Verify that you wrote the image to the disk (/dev/sdb), not a partition (/dev/sdb1). Also confirm the ISO download with its SHA256 checksum; a truncated or corrupted download produces an unbootable stick.

dd: invalid number: ‘4M’
Older or minimal systems may ship a dd that does not accept the M, G, or K suffixes. On those systems, spell out the block size in bytes (bs=4194304 for 4 MB), or install GNU coreutils.

FAQ

What does dd stand for?
The name comes from the IBM Job Control Language statement “Data Definition.” Because of how often the command is used to overwrite disks by mistake, many Linux users jokingly call it “disk destroyer.” Either way, the behavior is the same: it copies bytes from input to output without touching the filesystem layer.

Is dd faster than cp for copying large files?
For regular files on a normal filesystem, cp is usually just as fast and safer to use. dd only wins when you are copying raw devices, working with exact byte offsets, or deliberately limiting how much data is copied with count.

Can I use dd on an SSD without damaging it?
Reading and writing with dd is fine. The concern with SSDs is full-disk wipes: a single pass of zeros is enough to erase visible data, but repeated full-drive writes wear out the flash cells. For secure erase, use blkdiscard or the drive’s built-in secure-erase command.

How do I see how much data dd has copied?
Add status=progress to the command, or send the running process the USR1 signal with sudo kill -USR1 $(pidof dd). Both methods print the byte count and current throughput without interrupting the copy.

Why use bs=4M instead of the default?
The default block size of 512 bytes means dd issues a separate read and write for every 512 bytes of data. Larger block sizes such as 4 MB dramatically reduce syscall overhead and let the kernel fill the disk pipeline efficiently, often cutting copy times by a factor of ten.

Conclusion

dd is one of the most direct tools in the Linux toolbox. It copies bytes, no more and no less, between any two files or devices. That power makes it indispensable for flashing installers, cloning disks, and building image backups, and the same power makes it unforgiving if you point it at the wrong target.

For related disk tools, see the guides on fdisk for partitioning and df for checking free space.

How to Install and Use uv: Fast Python Package Manager

If you have worked with Python for a while, you have probably juggled pip, virtualenv, pip-tools, pipx, and pyenv just to keep a few projects running. Each tool does one thing, and wiring them together is slow and fragile. uv is a single binary that replaces all of them. It is written in Rust, published by Astral (the team behind ruff), and is typically ten to a hundred times faster than pip for common workflows.

This guide explains how to install uv on Linux, create and manage Python projects with it, add dependencies, work with virtual environments, and install specific Python versions.

Quick Reference

Command Description
uv init my-project Create a new project with pyproject.toml
uv add requests Add a dependency and update the lockfile
uv add --dev pytest Add a development dependency
uv remove requests Remove a dependency
uv sync Install dependencies from the lockfile
uv lock Update the lockfile
uv run script.py Run a command in the project environment
uv venv Create a virtual environment
uv pip install requests Pip-compatible install interface
uv python install 3.12 Install a specific Python version
uv python list List installed and available Python versions
uv tool install ruff Install a CLI tool globally
uvx ruff check . Run a tool without installing it permanently

Installing uv

The recommended install path on Linux is the standalone script from Astral. It downloads a prebuilt binary, places it in ~/.local/bin, and does not require Python to be installed first:

Terminal
curl -LsSf https://astral.sh/uv/install.sh | sh

The script prints where it put the binary and whether it updated your shell rc file. Reload your shell or run source ~/.bashrc so the new PATH takes effect, then verify the install:

Terminal
uv --version
output
uv 0.11.11

If you prefer to install from a package manager, uv is also available through pip, pipx, and Homebrew:

Terminal
pip install uv
Terminal
pipx install uv
Terminal
brew install uv

On Ubuntu, Debian, and Derivatives, pip may refuse a system-wide install because the Python environment is marked as externally managed. In that case, use pipx install uv or the standalone installer.

If you installed uv with the standalone installer, update it to the latest release with:

Terminal
uv self update

Creating a Project

uv init scaffolds a new project with a pyproject.toml, a README.md, a .python-version file, and a starter module:

Terminal
uv init my-project
cd my-project
output
Initialized project `my-project`

The generated pyproject.toml looks like this:

pyproject.tomltoml
[project]
name = "my-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []

uv has not created a virtual environment yet. That happens the first time you add a dependency or run a command in the project.

Adding and Removing Dependencies

To add a package, use uv add:

Terminal
uv add requests
output
Resolved 5 packages in 320ms
Installed 5 packages in 45ms
+ certifi==2025.1.31
+ charset-normalizer==3.4.1
+ idna==3.10
+ requests==2.32.3
+ urllib3==2.3.0

On the first run, uv creates a .venv directory in the project root, resolves the dependency graph, writes a uv.lock file that pins every package and its transitive dependencies, and installs everything into the venv. The requests line is also added to pyproject.toml under dependencies.

Development-only dependencies go into a separate group with --dev:

Terminal
uv add --dev pytest ruff

They are recorded under a dev dependency group in pyproject.toml and are installed into the same venv, but they are excluded when your project is published or installed as a library.

Remove a dependency with uv remove:

Terminal
uv remove requests

uv updates pyproject.toml, updates uv.lock, and uninstalls the package along with any dependencies that are no longer needed.

Running Code in the Project Environment

You do not have to activate the virtual environment manually. uv run executes a command inside the project venv and syncs dependencies first if needed:

Terminal
uv run python main.py
Terminal
uv run pytest

Scripts declared in pyproject.toml under [project.scripts] are also available:

Terminal
uv run my-command

If you prefer the classic workflow, activate the venv yourself:

Terminal
source .venv/bin/activate
python main.py

Either approach works, but uv run has the advantage of keeping the environment in sync with uv.lock every time you invoke it.

Syncing and Reproducing an Environment

When you clone a project that uses uv, run uv sync to install exactly the versions pinned in uv.lock:

Terminal
uv sync
output
Resolved 12 packages in 5ms
Installed 12 packages in 110ms

uv sync is deterministic: given the same uv.lock, it produces the same environment on any machine. This is the command to run in CI, in Docker builds, and on every fresh checkout.

If pyproject.toml changes but the lockfile is out of date, refresh the lock with:

Terminal
uv lock

Use uv lock --upgrade to bump every dependency to the latest version allowed by the version constraints in pyproject.toml, and uv lock --upgrade-package requests to bump a single package.

Working with Virtual Environments Directly

uv can also be used as a faster drop-in for virtualenv. To create a standalone venv in the current directory:

Terminal
uv venv
output
Using Python 3.12.8
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

Specify a Python version with --python:

Terminal
uv venv --python 3.11

Specify a different path:

Terminal
uv venv /tmp/myenv

Once the venv exists, use uv pip as a fast replacement for the regular pip commands:

Terminal
uv pip install requests
uv pip install -r requirements.txt
uv pip freeze
uv pip list

The uv pip interface is intentionally compatible with pip, which makes it easy to adopt uv in existing projects without restructuring them as pyproject.toml-based projects.

For a refresher on classic virtual environments, see the guide on Python virtual environments .

Installing and Managing Python Versions

uv can download and manage Python interpreters without touching your system Python. List the versions available and installed:

Terminal
uv python list

Install a specific version:

Terminal
uv python install 3.12
Terminal
uv python install 3.11 3.13

The downloaded interpreters live under ~/.local/share/uv/python/ and are independent of the system package manager. To use a specific version in a project, set it in the .python-version file or pass --python when creating the venv:

Terminal
uv venv --python 3.13

When uv run starts a project, it reads .python-version and the requires-python constraint in pyproject.toml, then picks or downloads a matching interpreter automatically.

Installing CLI Tools

uv ships with a tool manager similar to pipx. It installs Python-based CLIs in isolated environments so they do not pollute your system Python:

Terminal
uv tool install ruff
output
Resolved 1 package in 80ms
Installed 1 package in 12ms
+ ruff==0.9.2
Installed 1 executable: ruff

The ruff binary is now on your PATH. List, upgrade, or remove tools:

Terminal
uv tool list
uv tool upgrade ruff
uv tool uninstall ruff

To run a tool without installing it permanently, use uvx (an alias for uv tool run):

Terminal
uvx ruff check .

uvx downloads the tool into a cached environment, runs it, and reuses the cache on subsequent invocations. This is the fastest way to try a CLI without making it permanent.

Running Single-File Scripts

A Python script can declare its dependencies inline with a PEP 723 comment block. uv run reads that block and sets up an ephemeral environment before executing the script:

script.pypy
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "requests",
# ]
# ///

import requests

response = requests.get("https://api.github.com")
print(response.status_code)

Run the script with:

Terminal
uv run script.py

uv creates a temporary venv, installs requests, runs the script, and keeps the environment in a cache for next time. This pattern is useful for small utilities and one-off automation where a full project is overkill.

Troubleshooting

uv: command not found after install
The installer places the binary in ~/.local/bin, which may not be on your PATH. Add it in your shell rc file: export PATH="$HOME/.local/bin:$PATH", then reload the shell. Verify with which uv.

Installation fails with “error: externally-managed-environment”
This message comes from system pip, not from uv. Use pipx install uv or the standalone installer instead. Installing Python packages into the system Python is discouraged on modern Ubuntu and Debian releases.

uv sync installs different versions than expected
Check that uv.lock is committed to your repository. Without the lockfile, uv sync falls back to resolving from pyproject.toml and may pick newer versions. Commit uv.lock to keep environments reproducible across machines.

uv cannot find a suitable Python interpreter
Run uv python install 3.12 (or whichever version your project requires) to let uv download a matching interpreter. The requires-python field in pyproject.toml tells uv which versions are acceptable.

Permission denied when installing to the system
uv does not need root. If a command prompts for sudo, the destination path is wrong. Use a project virtual environment or uv tool install for CLIs instead of writing to system directories.

FAQ

How is uv different from pip?
pip installs packages into an existing Python environment. uv is a full project manager: it creates and manages virtual environments, locks dependencies, installs Python interpreters, and runs scripts and tools. uv pip also provides a drop-in pip-compatible command for teams that want just the speed without adopting the full workflow.

Does uv replace Poetry or Hatch?
For most projects, yes. uv reads the standard pyproject.toml format used by Poetry and Hatch, manages lockfiles, and handles dependency groups. If you depend on specific features of Poetry plugins or Hatch build hooks, evaluate those needs before migrating.

Is uv compatible with existing requirements.txt workflows?
Yes. uv pip install -r requirements.txt works as a faster replacement for pip install -r, and uv pip compile generates a pinned requirements file from your unpinned input, similar to pip-compile from pip-tools.

Can I use uv with Docker?
Yes, and it is a good fit. The recommended pattern is to copy pyproject.toml and uv.lock first, run uv sync --frozen --no-install-project to install dependencies, then copy the rest of the source and run uv sync --frozen. This keeps the dependency layer cached across builds.

Where does uv store downloaded interpreters and package cache?
Interpreters go under ~/.local/share/uv/python/ and the package cache lives in ~/.cache/uv/. Both paths respect the XDG environment variables, so you can override them by setting XDG_DATA_HOME and XDG_CACHE_HOME.

Conclusion

uv rolls together the jobs that previously required pip, virtualenv, pip-tools, pipx, and pyenv into one fast binary. The speed is the headline feature, but the real payoff is a single, consistent workflow for projects, tools, scripts, and Python versions.

For related Python tooling on Linux, see the guide on installing Python on Ubuntu 24.04 and the guide on Python virtual environments .

How to Install Python on Ubuntu 26.04

When you start a Python project on Ubuntu 26.04, you already have a Python interpreter available. The usual setup work is installing pip, adding the venv module, or installing a separate Python version for a project that needs something other than Ubuntu’s default Python.

This guide explains how to install Python tooling on Ubuntu 26.04, how to use the deadsnakes PPA for alternate Python versions, and how to build Python from source when you need a specific upstream release.

Ubuntu 26.04 Default Python Version

Ubuntu 26.04 ships with Python 3.14 as the default Python 3 interpreter. To check the version on your system, run:

Terminal
python3 --version
output
Python 3.14.1

The exact patch version may change as Ubuntu publishes updates, but the default interpreter remains Python 3.14. Most users should keep this interpreter in place and use virtual environments for project packages.

Do not replace the /usr/bin/python3 interpreter. Ubuntu tools expect the distribution-provided Python version. If a project needs a different Python release, install it alongside the default interpreter and call it by its versioned command, such as python3.13 or python3.15.

Quick Reference

Task Command
Check Ubuntu’s default Python version python3 --version
Install pip and venv for the default Python sudo apt install python3-pip python3-venv
Create a virtual environment with the default Python python3 -m venv myproject
Add deadsnakes PPA sudo add-apt-repository ppa:deadsnakes/ppa
Install Python 3.13 from PPA sudo apt install python3.13
Install Python 3.15 preview from PPA sudo apt install python3.15
Install venv module for PPA Python sudo apt install python3.13-venv
Create a virtual environment with PPA Python python3.13 -m venv myproject
Activate virtual environment source myproject/bin/activate
Deactivate virtual environment deactivate
Build from source ./configure --enable-optimizations && make -j $(nproc)
Install source build safely sudo make altinstall

Installing pip and venv for the Default Python

If Ubuntu’s default Python 3.14 is enough for your work, install pip and the virtual environment module from the Ubuntu repositories.

First, update the package index:

Terminal
sudo apt update

Install pip and the virtual environment module:

Terminal
sudo apt install python3-pip python3-venv

Verify the installed commands:

Terminal
python3 --version
pip3 --version

On Ubuntu 26.04, python3 points to Python 3.14. Use a virtual environment for project dependencies instead of installing packages into the system Python environment.

Create a virtual environment with the default Python:

Terminal
python3 -m venv myproject

Activate it:

Terminal
source myproject/bin/activate

Inside the environment, python and pip point to the isolated project environment:

Terminal
python --version
python -m pip --version

When you are done working in the project, deactivate the environment:

Terminal
deactivate

Installing Python from the Deadsnakes PPA

The deadsnakes PPA provides alternate Python versions packaged for Ubuntu. It now supports Ubuntu 26.04, including packages for Python versions that Ubuntu does not provide as the default interpreter.

Deadsnakes does not provide Python 3.14 for Ubuntu 26.04 because Ubuntu already includes Python 3.14. Use the PPA when you need another interpreter, such as Python 3.13 or the Python 3.15 preview builds.

  1. Install the prerequisites and add the PPA:

    Terminal
    sudo apt update
    sudo apt install software-properties-common
    sudo add-apt-repository ppa:deadsnakes/ppa

    Press Enter when prompted to confirm.

  2. Install Python 3.13:

    Terminal
    sudo apt update
    sudo apt install python3.13

    To install the Python 3.15 preview package instead, replace python3.13 with python3.15:

    Terminal
    sudo apt install python3.15

    Python 3.15 is still a preview release, so use it for testing rather than production workloads.

  3. Verify the installation:

    Terminal
    python3.13 --version

    The command prints the installed Python 3.13 patch release. You can also confirm the binary location:

    Terminal
    which python3.13
  4. Install the venv module for the same interpreter:

    Terminal
    sudo apt install python3.13-venv

    If you installed Python 3.15, use:

    Terminal
    sudo apt install python3.15-venv
  5. Create a virtual environment and use pip inside it:

    Terminal
    python3.13 -m venv myproject
    source myproject/bin/activate
    python -m pip install --upgrade pip
Info
The system default python3 still points to Python 3.14. To use a PPA version, run python3.13, python3.15, or another explicit version command.

Installing Python from Source

Compiling Python from source allows you to install any version and customize the build options. However, you will not be able to manage the installation through the apt package manager.

The following steps show how to compile Python 3.14.4. If you are installing a different version, replace the version number in the commands below.

  1. Install the libraries and dependencies required to build Python:

    Terminal
    sudo apt update
    sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev liblzma-dev
  2. Download the source code from the Python download page using wget :

    Terminal
    wget https://www.python.org/ftp/python/3.14.4/Python-3.14.4.tgz
  3. Extract the archive :

    Terminal
    tar -xf Python-3.14.4.tgz
  4. Navigate to the source directory and run the configure script:

    Terminal
    cd Python-3.14.4
    ./configure --enable-optimizations

    The --enable-optimizations flag runs profile-guided optimization tests, which makes the build slower but produces a faster Python binary.

  5. Start the build process:

    Terminal
    make -j $(nproc)

    The -j $(nproc) option uses all available CPU cores for a faster build.

  6. Install the Python binaries using altinstall. Do not use install, as it overwrites the system python3 binary and can break system tools that depend on it:

    Terminal
    sudo make altinstall
  7. Verify the installation:

    Terminal
    python3.14 --version
    output
    Python 3.14.4

Setting Up a Virtual Environment

After installing a new Python version, create a virtual environment for your project to keep dependencies isolated:

Terminal
python3.13 -m venv myproject

Activate the virtual environment:

Terminal
source myproject/bin/activate

Your shell prompt will change to show the environment name. Inside the virtual environment, python and pip point to the version you used to create it.

For a source-built Python 3.14 installation, use the versioned command:

Terminal
python3.14 -m venv myproject

To deactivate the virtual environment:

Terminal
deactivate

For more details, see our guide on how to create Python virtual environments .

Optional: Uninstalling the PPA Version

To remove the PPA version:

Terminal
sudo apt remove python3.13 python3.13-venv

To remove the PPA itself:

Terminal
sudo add-apt-repository --remove ppa:deadsnakes/ppa

Troubleshooting

python3 already exists after installation
This is expected. Ubuntu 26.04 includes Python 3.14 by default, so the default setup usually adds missing tools such as pip3 or venv.

pip reports an externally managed environment
Ubuntu protects system-managed Python packages. Create a virtual environment with python3 -m venv myproject, activate it, and install packages with python -m pip install package-name inside the environment.

No module named venv
Install the matching venv package. For Ubuntu’s default Python, run sudo apt install python3-venv. For Python 3.13 from the PPA, run sudo apt install python3.13-venv.

Unable to locate package python3.13 or python3.15
Run sudo apt update after adding the deadsnakes PPA. If the package is still unavailable, check that the PPA supports your Ubuntu release and CPU architecture.

python3 still shows Python 3.14
The python3 command should continue to use Ubuntu’s default interpreter. Run the alternate version with its versioned command, such as python3.13 or python3.15.

Python 3.15 shows an alpha version
Python 3.15 is still a preview release. Use it for testing compatibility, and use Python 3.14 or another stable release for production projects.

FAQ

Should I use the PPA or build from source?
Use the Ubuntu packages if Python 3.14 is enough. Use the deadsnakes PPA when you need an alternate packaged interpreter such as Python 3.13 or a Python 3.15 preview. Build from source only if you need a specific upstream patch release or a custom build configuration.

Will installing a new Python version break my system?
No. Both methods install the new version alongside the system Python 3.14. The system python3 command is not affected. You access the alternate version with python3.13, python3.15, or another explicit version command.

How do I make the new Python version the default?
You can use update-alternatives to configure it, but this is not recommended. Many Ubuntu system tools depend on the default python3 being the version that shipped with the OS. Use virtual environments instead.

How do I install pip for the new Python version?
The recommended approach is to use a virtual environment. Install the matching venv package, create a venv, activate it, and use python -m pip inside the environment.

What is the difference between install and altinstall when building from source?
altinstall installs a versioned binary such as python3.14 without creating a python3 symlink. install creates the symlink, which overwrites the system Python and can break Ubuntu system tools.

Does Ubuntu 26.04 include pip by default?
Ubuntu 26.04 includes Python 3.14 but does not include pip in the base installation. Install it with sudo apt install python3-pip. For alternate Python versions, use python -m pip inside a virtual environment.

Conclusion

Stick with Ubuntu’s Python 3.14 plus python3-pip and python3-venv for most projects. Reach for the deadsnakes PPA when a project pins to 3.13 or wants to test the 3.15 preview, and build from source only for a specific upstream patch.

For more on Python package management, see our guide on how to use pip .

fdisk Cheatsheet

Basic Usage

Open a disk or print partition tables.

Command Description
sudo fdisk /dev/sdX Open a disk in interactive mode
sudo fdisk -l List all detected partition tables
sudo fdisk -l /dev/sdX List one disk partition table
sudo fdisk --help Show command-line options
sudo fdisk --version Show fdisk version

List Disks

Identify the correct device before changing partitions.

Command Description
lsblk Show disks, partitions, and mount points
lsblk -d -o NAME,SIZE,MODEL Show whole disks only
lsblk -f Show filesystems and UUIDs
sudo fdisk -l Show partition tables with disk labels
sudo fdisk -x /dev/sdX Show detailed partition information

Interactive Commands

Commands used inside the fdisk prompt.

Command Description
m Show help menu
p Print the current partition table
n Create a new partition
d Delete a partition
t Change partition type
l List available partition types
w Write changes and exit
q Quit without saving

Partition Tables

Create a new disk label before adding partitions on a blank disk.

Command Description
g Create a new GPT partition table
o Create a new MBR (DOS) partition table
p Review the current table before saving
w Write the new table to disk
q Exit without writing changes

Create Partitions

Common answers while creating a new partition with n.

Input Description
n Start a new partition
Enter Accept the default partition number
Enter Accept the default first sector
+1G Create a 1 GiB partition
+100G Create a 100 GiB partition
Enter Use the rest of the available disk space
p Print the proposed layout
w Save the changes

Partition Types

Set the partition type when the partition is not a regular Linux data partition.

Input Description
l List partition types
t Change a partition type
Linux filesystem Regular Linux data partition (GPT)
Linux swap Swap partition (GPT)
EFI System EFI System partition (GPT)
Linux LVM LVM physical volume (GPT)
Linux RAID Linux RAID member (GPT)
83 Regular Linux partition (MBR)
82 Swap partition (MBR)
8e LVM partition (MBR)

Review and Save

Check the in-memory table before writing it to disk.

Command Description
p Print the pending partition table
v Verify the partition table
i Show details about a partition
w Write changes to disk and exit
q Quit without saving changes

Format and Mount

After writing the partition table, create a filesystem and mount the partition.

Command Description
sudo mkfs.ext4 /dev/sdX1 Format a partition as ext4
sudo mkswap /dev/sdX2 Create swap on a partition
sudo swapon /dev/sdX2 Enable swap
sudo mkdir -p /mnt/data Create a mount point
sudo mount /dev/sdX1 /mnt/data Mount the partition
lsblk -f Confirm filesystem and mount details

Safety Checks

Commands that help avoid editing the wrong disk.

Command Description
lsblk -d -o NAME,SIZE,MODEL Compare disk names, sizes, and models
lsblk -f Check existing filesystems and mount points
sudo fdisk -l /dev/sdX Review the current table before editing
mount | grep /dev/sdX Check whether partitions are mounted
sudo umount /dev/sdX1 Unmount a partition before changing it
sudo partprobe /dev/sdX Ask the kernel to re-read the table

Related Tools

References for the full workflow around disks and partitions.

Tool Description
mount Mount and unmount filesystems
mkfs.ext4 Format a partition with a filesystem
df Check filesystem disk usage
fsck Check and repair filesystems

SFTP Cheatsheet

Connect and Authenticate

Open an SFTP session against a remote host.

Command Description
sftp user@hostname Connect to remote server
sftp user@192.168.1.10 Connect by IP
sftp -P 2222 user@hostname Connect to a custom SSH port
sftp -i ~/.ssh/id_ed25519 user@hostname Connect with a specific key
sftp -b commands.txt user@hostname Run commands from a batch file
quit Quit session
bye Quit session (alias for quit)

Local and Remote Paths

Navigate directories on both sides of the session.

Command Description
pwd Show remote working directory
lpwd Show local working directory
cd /remote/path Change remote directory
lcd /local/path Change local directory
ls List remote files
lls List local files
ls -la Long listing of remote files

Download Files

Pull files from the remote server to the local system.

Command Description
get file.txt Download one file
get remote.txt local.txt Download and rename locally
get -r remote_dir Download a directory recursively
get -P file.txt Preserve file permissions and timestamps
mget *.log Download multiple files matching a pattern
reget large.iso Resume an interrupted download

Upload Files

Push local files to the remote server.

Command Description
put file.txt Upload one file
put local.txt remote.txt Upload with a remote name
put -r local_dir Upload a directory recursively
put -P file.txt Preserve permissions and timestamps
mput *.txt Upload multiple files
reput large.iso Resume an interrupted upload

Remote File Management

Manage files and directories on the SFTP server.

Command Description
mkdir dirname Create remote directory
rmdir dirname Remove empty remote directory
rm file.txt Delete remote file
rename old.txt new.txt Rename remote file
ln source link Create a hard link on the remote
ln -s source link Create a symbolic link on the remote
df -h Show remote filesystem usage

Permissions and Ownership

Adjust permissions and ownership on remote files.

Command Description
chmod 644 file.txt Change remote file mode
chown 1000 file.txt Change remote owner by UID
chgrp 1000 file.txt Change remote group by GID
umask 022 Set default permission mask

Session Helpers

Inspect and control the active session.

Command Description
!command Run a local shell command
! Drop into a local shell
version Show SFTP protocol version
progress Toggle transfer progress meter
help or ? List available SFTP commands

Non-Interactive and Scripting

Use SFTP from scripts and automated jobs.

Command Description
sftp -b script.txt user@host Run a batch file of commands
sftp -q user@host Quiet mode (suppress banner and progress)
sftp -o IdentityFile=key user@host Pass any ssh_config option
echo "get file.txt" | sftp -b - user@host Pipe commands via stdin

Related Tools

Other ways to move files between systems.

Tool Description
scp Secure copy over SSH
rsync Efficient sync and incremental transfer
ssh Underlying secure shell protocol
ftp Legacy unencrypted file transfer

Bash Split String: Split a String by Delimiter

When a script reads a CSV row, a colon-separated config line, or a piece of user input, one of the first things you usually need to do is break that string into its individual fields. Bash does not have a dedicated split function, but the shell gives you several ways to do the job with nothing more than built-ins and common core utilities.

This guide explains how to split a string by a delimiter in Bash using read, the IFS variable, tr, awk, and parameter expansion, with safe examples you can drop straight into a script.

The IFS Variable

Word splitting in Bash is controlled by the internal field separator, stored in the IFS variable. By default, IFS contains a space, a tab, and a newline, which is why unquoted expansions split on whitespace. When you set IFS to a single character such as a comma or a colon, Bash uses that character to decide where one field ends and the next begins.

Most of the techniques below work by changing IFS for one command only, so the rest of the script keeps the default behavior.

Splitting a String with read

The cleanest way to split a string into named variables is the read built-in combined with a temporary IFS:

sh
#!/bin/bash

STR="ubuntu:debian:fedora:arch"
IFS=":" read -r first second third fourth <<< "$STR"

echo "$first"
echo "$second"
echo "$third"
echo "$fourth"

Running the script produces one field per line:

output
ubuntu
debian
fedora
arch

The IFS=":" assignment applies only to the read command that follows it, so the global IFS stays untouched. The here-string (<<<) feeds the string into read on standard input, and -r keeps backslashes literal so they are not treated as escape characters.

Splitting Into a Bash Array

When the number of fields is not known ahead of time, split the string into an array instead of a fixed list of variables. Pass -a to read to tell it the target is an array:

sh
#!/bin/bash

STR="1,2,3,4,5"
IFS="," read -r -a numbers <<< "$STR"

for n in "${numbers[@]}"; do
 echo "$n"
done

The numbers array now holds one value per comma-separated field, and the loop prints each one on its own line. Always expand arrays with "${numbers[@]}" inside double quotes so elements that contain spaces stay intact.

For a deeper look at what else arrays can do, see our guide on Bash arrays .

Splitting With a Multi-Character Delimiter

IFS treats each character in its value as a separate delimiter, so setting IFS="::" will still split on single colons. For a multi-character delimiter such as :: or -, convert it to a single character first with sed or use parameter expansion:

sh
#!/bin/bash

STR="alpha::beta::gamma"
IFS="|" read -r -a parts <<< "${STR//::/|}"

for p in "${parts[@]}"; do
 echo "$p"
done

The ${STR//::/|} expansion replaces every occurrence of :: with a pipe character, and then read splits the resulting string on the pipe. Pick a placeholder character that you are sure the input does not contain.

Splitting With tr

When you only need the fields printed one per line, tr is often enough. It translates the delimiter into a newline:

Terminal
printf '%s\n' "red,green,blue" | tr ',' '\n'
output
red
green
blue

This approach pairs well with pipelines. For example, you can count the number of fields by piping the output into wc -l, or loop over the values with a while read block:

sh
#!/bin/bash

STR="red,green,blue"

while IFS= read -r color; do
 echo "Color: $color"
done < <(printf '%s\n' "$STR" | tr ',' '\n')

The process substitution <(...) sends the output of the pipeline into the loop without using a subshell for the loop body, so any variables you set inside the loop remain available after it ends.

Splitting With awk

For more structured splitting, awk is the right tool. It reads input one record at a time and exposes each field as $1, $2, and so on. Use -F to set the field separator:

Terminal
echo "jane:x:1001:1001:Jane Doe:/home/jane:/bin/bash" | awk -F':' '{ print $1, $5 }'
output
jane Jane Doe

awk is also a good pick when the delimiter is a regular expression, when you need to skip a header line, or when you want to act on a specific field without touching the rest. For more patterns, see our awk command guide .

Splitting With Parameter Expansion

Bash parameter expansion can pull a prefix or a suffix off a string without calling any external command. This is the fastest option when you only need one part, not all of them:

sh
#!/bin/bash

PATH_LINE="/usr/local/bin/script.sh"

BASE="${PATH_LINE##*/}"
DIR="${PATH_LINE%/*}"

echo "Directory: $DIR"
echo "Basename: $BASE"
output
Directory: /usr/local/bin
Basename: script.sh

##*/ strips the longest match of */ from the start of the string, leaving the basename. %/* strips the shortest match of /* from the end, leaving the directory. The same pattern works for any delimiter, not only the slash.

For full coverage of these expansions, see our guide on Bash string manipulation .

Preserving Empty Fields

A common surprise with read is that trailing empty fields can be dropped. Consider the string a,,b,:

sh
#!/bin/bash

STR="a,,b,"
IFS="," read -r -a fields <<< "${STR},"

echo "Count: ${#fields[@]}"
printf '[%s]\n' "${fields[@]}"
output
Count: 4
[a]
[]
[b]
[]

Bash keeps empty fields in the middle, but read -a drops a trailing empty field when the delimiter is the last character. Appending one extra delimiter before reading gives read a final empty value to assign, which preserves the trailing field from the original string.

Quick Reference

The table below summarizes which technique to reach for based on what the script needs to do.

Goal Technique
Split into named variables IFS=":" read -r a b c <<< "$STR"
Split into an array IFS="," read -r -a arr <<< "$STR"
Multi-character delimiter Replace with ${STR//::/|} first, then split
Print one field per line printf '%s\n' "$STR" | tr ',' '\n'
Select specific fields awk -F',' '{ print $2 }'
Take prefix or suffix only ${STR%%...} / ${STR##...}

For a printable quick reference, see the bash cheatsheet .

FAQ

How do I split a string by whitespace in Bash?
Leave IFS at its default and use read -r -a arr <<< "$STR". Any run of spaces, tabs, or newlines will act as a separator.

Why does my array lose the last empty field?
If the delimiter is at the very end of the string, read -a can drop the trailing empty element. Append one extra delimiter before reading, for example IFS=',' read -r -a arr <<< "${STR},", when the trailing empty field matters.

Can I split on a newline?
Yes. For line-based strings, use mapfile -t lines <<< "$STR" so each input line becomes one array element. Plain read stops at the first newline unless you change how it reads the input.

Is there a built-in split function in Bash?
No. Word splitting driven by IFS plus the read built-in is the idiomatic replacement for a dedicated split function.

Conclusion

Splitting strings in Bash comes down to picking the right tool for the shape of the input: read with IFS for structured parsing, tr or awk for pipelines, and parameter expansion for quick prefix or suffix work. When the input is untrusted, quote every expansion and handle empty fields on purpose rather than by accident.

cd Cheatsheet

Basic Syntax

Core command forms for changing directories.

Command Description
cd [DIRECTORY] Change to a directory
cd Change to your home directory
cd -- DIRECTORY Change to a directory whose name may start with -
pwd Print the current working directory

Everyday Navigation

Common ways to move around the filesystem.

Command Description
cd /etc Change to an absolute path
cd Downloads Change to a relative path
cd .. Move up one directory
cd ../.. Move up two directories
cd ./scripts Change to a directory under the current directory

Home Directories

Use shell shortcuts for your home directory and other users’ homes.

Command Description
cd ~ Change to your home directory
cd ~/Downloads Change to Downloads inside your home directory
cd ~username Change to another user’s home directory
cd "$HOME" Change to the directory stored in $HOME

Relative Paths

Build paths from your current directory.

Command Description
cd . Stay in the current directory
cd .. Move to the parent directory
cd ../src Move up one level, then into src
cd ../../var Move up two levels, then into var
cd project/docs Move through nested directories

Previous Directory

Switch between recently used directories.

Command Description
cd - Change to the previous working directory
echo "$OLDPWD" Show the previous working directory
cd "$OLDPWD" Change to the previous directory without using cd -
pushd /path Change directory and save the old one on the stack
popd Return to a directory from the stack

Paths with Spaces

Quote or escape paths that contain spaces or shell metacharacters.

Command Description
cd "Project Files" Quote a directory name with spaces
cd 'Project Files' Use single quotes for a literal path
cd Project\ Files Escape the space with a backslash
cd -- "-reports" Enter a directory whose name starts with -

Symlinks and Physical Paths

Control whether cd follows logical or physical paths.

Command Description
cd -L linkdir Follow symbolic links (default in Bash)
cd -P linkdir Resolve to the physical directory path
pwd Show the shell’s logical current directory
pwd -P Show the physical current directory
cd -P .. Move using the physical directory structure

CDPATH

Search extra base directories when changing by name.

Command Description
export CDPATH=.:~/projects:/opt Search current directory, ~/projects, and /opt
cd myapp Try matching myapp in each CDPATH entry
unset CDPATH Disable CDPATH for the current shell
CDPATH= cd myapp Run one cd command without CDPATH

Troubleshooting

Quick checks for common directory-change errors.

Issue Check
No such file or directory Verify the path with ls -ld path
Permission denied Check execute permission on the directory
Path with spaces fails Quote the path or escape spaces
cd - fails $OLDPWD is not set yet
Unexpected target with CDPATH Run unset CDPATH or use an absolute path
Symlink path looks different Compare pwd and pwd -P

Related Guides

Use these guides for detailed directory navigation workflows.

Guide Description
cd Command in Linux: Change Directories Full cd guide with examples
How to Get the Current Working Directory in Linux Use pwd and understand the current directory
pushd and popd Commands in Linux Work with the directory stack
Linux Commands Cheatsheet General Linux command quick reference

Initial Server Setup on Ubuntu 26.04

A fresh Ubuntu 26.04 server ships with root SSH access, no regular user, and no firewall rules. That works for the first login, but it is not a safe state to leave running on a public VPS.

This guide walks through the first tasks to perform on a new Ubuntu 26.04 server: creating a sudo user, enabling SSH key authentication, locking down SSH, configuring UFW, setting the hostname and timezone, and applying package updates.

Quick Reference

Task Command or file
Log in as root ssh root@server_ip_address
Create a user adduser username
Grant sudo access usermod -aG sudo username
Copy root SSH keys rsync --archive --chown=username:username /root/.ssh /home/username
Add a local key ssh-copy-id username@server_ip_address
SSH hardening file /etc/ssh/sshd_config.d/99-hardening.conf
Test SSH config sudo sshd -t
Allow SSH in UFW sudo ufw allow OpenSSH
Set hostname sudo hostnamectl set-hostname server-name
Set timezone sudo timedatectl set-timezone Europe/Berlin

Prerequisites

Before starting, make sure you have:

  • A new Ubuntu 26.04 server with a public IP address.
  • Root access over SSH, either with a password or a provider-supplied key.
  • A local SSH key pair on your workstation. If you do not have one yet, see how to generate SSH keys on Linux .
  • Access to the provider web console as a backup path in case SSH access stops working.

Keep your original root SSH session open until you have tested the new user login and the hardened SSH configuration.

Log In as Root

Open a terminal on your local machine and connect to the server using the public IP address from your hosting provider:

Terminal
ssh root@server_ip_address

Accept the host key when prompted and enter the root password if password authentication is still enabled. If your provider created the server with an SSH key, the connection should use that key automatically.

Create a New Sudo User

Working as root for daily administration is risky because every command runs with full privileges. Create a regular user account and give it administrative access through the sudo group.

Replace username with the account name you want to use:

Terminal
adduser username

The command prompts for a password and optional user details. Enter a strong password, then press Enter to skip any fields you do not need.

Add the new user to the sudo group:

Terminal
usermod -aG sudo username

The account can now run administrative commands with sudo.

Set Up SSH Key Authentication

SSH keys are safer than password logins and are easier to use once configured. The exact command depends on where your public key is currently stored.

If your public key is already present under the root account, copy the root SSH directory to the new user:

Terminal
rsync --archive --chown=username:username /root/.ssh /home/username

If you need to copy a key from your local workstation, run this command from the local machine:

Terminal
ssh-copy-id username@server_ip_address

Open a new terminal window and test the login before changing the SSH server configuration:

Terminal
ssh username@server_ip_address

The connection should succeed as the new user. Keep both the root session and the new user session open while you continue.

Disable Root Login and Password Authentication

After key-based login works, configure OpenSSH to reject direct root logins and password authentication. Ubuntu includes files from /etc/ssh/sshd_config.d/, which keeps local changes separate from the main SSH configuration file.

Create a hardening snippet:

Terminal
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf

Add the following lines:

/etc/ssh/sshd_config.d/99-hardening.conftxt
PermitRootLogin no
PasswordAuthentication no

Save the file and test the SSH configuration syntax:

Terminal
sudo sshd -t

If the command prints no output, the configuration is valid. Reload SSH to apply the change:

Terminal
sudo systemctl reload ssh

Open another terminal and confirm that you can still log in as the regular user:

Terminal
ssh username@server_ip_address

Do not close your existing sessions until this test succeeds.

Set Up the Firewall with UFW

Ubuntu uses UFW (Uncomplicated Firewall) as a simple front-end for managing host firewall rules. Start by allowing SSH so the firewall does not block your current access:

Terminal
sudo ufw allow OpenSSH

Enable the firewall:

Terminal
sudo ufw enable

Confirm the prompt with y, then check the active rules:

Terminal
sudo ufw status

The output should show that OpenSSH is allowed:

output
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)

When you install services such as Nginx or Apache, open their profiles before expecting traffic to reach them. For example, an Nginx server that should accept HTTP and HTTPS traffic needs:

Terminal
sudo ufw allow 'Nginx Full'

For more examples, see how to set up a firewall with UFW .

Set the Hostname

A descriptive hostname makes logs, shell prompts, monitoring alerts, and dashboards easier to read. Set the hostname with hostnamectl:

Terminal
sudo hostnamectl set-hostname server-name

Replace server-name with a short name that matches the server role, such as web-01 or db-01.

Check the result:

Terminal
hostnamectl

You can update DNS records or your local SSH config separately if you want to connect by name instead of IP address.

Set the Timezone

Set the server timezone so logs, cron jobs, and timestamps match the region you use for operations:

Terminal
sudo timedatectl set-timezone Europe/Berlin

List available zones if you are unsure of the exact name:

Terminal
timedatectl list-timezones

See how to set or change the timezone on Ubuntu for a deeper explanation.

Update the System

Refresh the package index and install pending updates:

Terminal
sudo apt update
sudo apt upgrade

If the upgrade installed a new kernel or core system libraries, reboot the server:

Terminal
sudo reboot

After the reboot, reconnect as the regular sudo user:

Terminal
ssh username@server_ip_address

Troubleshooting

Locked out after disabling password authentication
Use your provider web console or recovery mode to log in. Edit /etc/ssh/sshd_config.d/99-hardening.conf, temporarily set PasswordAuthentication yes, run sudo sshd -t, reload SSH, and test key login again before disabling passwords.

usermod: group 'sudo' does not exist
Some minimal images may not include the sudo package. Install it with apt install sudo, then rerun usermod -aG sudo username.

sshd -t reports an error
Read the line number in the error message, fix the snippet in /etc/ssh/sshd_config.d/99-hardening.conf, and run sudo sshd -t again. Do not reload SSH until the syntax test passes.

UFW blocks an expected service
Check the active rules with sudo ufw status. Allow the needed service profile or port, such as sudo ufw allow 'Nginx Full' for Nginx web traffic, then test the connection again.

Conclusion

You now have an Ubuntu 26.04 server with a sudo user, key-based SSH access, direct root logins disabled, a basic firewall, and current packages. A good next step is to enable automatic security updates before installing the rest of your stack.

netstat Cheatsheet

Basic Syntax

Core netstat command forms and output controls.

Command Description
netstat Show active non-listening sockets
sudo netstat -a Show all sockets
sudo netstat -n Show numeric addresses and ports
sudo netstat -p Show PID and program name where available
sudo netstat -c Refresh output every second

Listening Ports

Find services that are accepting local connections.

Command Description
sudo netstat -tuln Show TCP and UDP listening sockets
sudo netstat -tulnp Show listening sockets with PID and process name
sudo netstat -tnlp Show listening TCP sockets only
sudo netstat -unlp Show listening UDP sockets only
sudo netstat -tulnp | grep ':80' Find the process listening on port 80

Connections and TCP States

Inspect active connections and common TCP states.

Command Description
sudo netstat -at Show all TCP sockets
sudo netstat -au Show all UDP sockets
sudo netstat -ant Show TCP sockets with numeric addresses
sudo netstat -ant | grep ESTABLISHED Show established TCP connections
sudo netstat -ant | grep TIME_WAIT Show TCP connections in TIME_WAIT
sudo netstat -atnc Watch TCP connection output continuously

Counts and Filters

Use shell filters with netstat output.

Command Description
sudo netstat -ant | wc -l Count TCP output rows
sudo netstat -ant | grep ':80' | wc -l Count TCP connections involving port 80
sudo netstat -ant | grep ESTABLISHED | wc -l Count established TCP connections
sudo netstat -tulnp | grep nginx Find sockets owned by nginx
sudo netstat -ant | grep 203.0.113.10 Filter connections by remote IP address

Routes, Interfaces, and Stats

Show routing, interface, and protocol information.

Command Description
netstat -rn Show the routing table with numeric addresses
netstat -r Show the routing table with name resolution
netstat -i Show interface counters
netstat -ie Show extended interface details
netstat -s Show protocol statistics
netstat -st Show TCP protocol statistics
netstat -su Show UDP protocol statistics

Modern Replacements

Use current Linux tools for new workflows and scripts.

netstat Command Modern Command
netstat -tuln ss -tuln
sudo netstat -tulnp sudo ss -tulnp
netstat -rn ip route
netstat -i ip -s link
netstat -s ss -s

Troubleshooting

Common netstat issues and quick fixes.

Issue Check
netstat: command not found Install the net-tools package or use ss
Process column is empty Run with sudo when using -p
Output is slow Add -n to disable name resolution
Port lookup matches too much Search with a colon, such as grep ':80'
Need listeners only Add -l with protocol flags such as -tuln

Related Guides

Use these guides for full walkthroughs and modern alternatives.

Guide Description
netstat Command in Linux Full netstat guide with examples
ss Command in Linux Modern socket inspection tool
ip Command in Linux Modern routes and interface management
How to Check Listening Ports in Linux Compare ss, netstat, and lsof
lsof Command in Linux Tie sockets and files back to processes

netstat Command in Linux: Network Connections and Statistics

When something on a server is holding port 80, a connection is refusing to close, or a service is not reachable from the outside, the first question is almost always the same: what is actually listening, and who is talking to whom. For years, the answer on Linux was the netstat command.

netstat is part of the classic net-tools package and prints network connections, listening ports, routing tables, interface counters, and per-protocol statistics. It has been deprecated in favor of ss and ip , but it is still installed on many systems and the tool many sysadmins reach for first. This guide explains how to read its output and which flags cover the cases you run into day to day.

Install netstat

On most modern distributions, net-tools is not installed by default. If netstat is not available, install it with your package manager.

On Ubuntu, Debian, and Derivatives:

Terminal
sudo apt install net-tools

On Fedora, RHEL, and Derivatives:

Terminal
sudo dnf install net-tools

Once the package is installed, verify the binary is on your path:

Terminal
netstat --version

netstat Syntax

The general form of the command is:

txt
netstat [OPTIONS]

Run without options, netstat prints a list of open non-listening sockets, which is rarely what you want. In practice, you almost always pass a combination of flags that describe what kind of sockets you care about (TCP, UDP, Unix), what state they are in, and how you want the output formatted.

List All Connections

To list every connection, both listening and established, use the -a option:

Terminal
sudo netstat -a

The output is divided into two parts. The top lists Internet sockets with columns for protocol, receive and send queue sizes, local and foreign address, and state. The bottom lists Unix domain sockets used for local inter-process communication.

Running netstat with sudo is recommended because without it, the command cannot read socket ownership for processes that belong to other users.

Show TCP and UDP Connections

The -t and -u flags filter the output by protocol. To show only TCP connections:

Terminal
sudo netstat -at

To show only UDP connections:

Terminal
sudo netstat -au

You can combine both flags to get TCP and UDP together:

Terminal
sudo netstat -atu

Show Listening Ports

When you want to know which services are accepting new connections, use the -l option. For TCP, it restricts the output to sockets in the LISTEN state. UDP does not use the same connection states, but UDP sockets that are ready to receive traffic are still shown.

Terminal
sudo netstat -tuln

The combined flags read as: show TCP (-t) and UDP (-u) sockets that are listening (-l), with numeric addresses and ports (-n). The output looks similar to this:

output
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN
tcp6 0 0 :::80 :::* LISTEN
udp 0 0 0.0.0.0:68 0.0.0.0:*

From the output, we can see that SSH is listening on all interfaces on port 22, MySQL is bound to localhost only on port 3306, and a web server is listening on port 80 over IPv6.

Show the Process Using a Port

To find out which program owns a socket, add the -p option. It prints the PID and the process name next to each connection:

Terminal
sudo netstat -tulnp
output
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 812/sshd
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 1034/mysqld
tcp6 0 0 :::80 :::* LISTEN 1591/nginx: master

The PID/Program name column ties each listening port to a process. This is the quickest way to find out which service is holding a port when you get an Address already in use error.

To check one specific port, pipe the output to grep :

Terminal
sudo netstat -tulnp | grep ':80'

If the command prints a row, a process is listening on port 80. If it prints nothing, no TCP or UDP listener matched that port on the local system.

Show Numeric Output

By default, netstat resolves IP addresses to hostnames and port numbers to service names (for example, 22 becomes ssh). On a busy system, this can be slow because each address needs a DNS lookup.

The -n option disables name resolution and prints raw numbers:

Terminal
sudo netstat -n

Numeric output is also easier to pipe into other tools such as grep or awk, because the field values are predictable.

Continuous Monitoring

The -c option tells netstat to print the output every second until you stop it with Ctrl+C. It is useful when you want to watch a connection appear, change state, and close:

Terminal
sudo netstat -atnc

Each iteration prints the full table again, so it is best used together with a filter such as grep to isolate a single connection.

Show TCP Connection States

TCP connections move through states such as ESTABLISHED, LISTEN, TIME_WAIT, and CLOSE_WAIT. To show active TCP sockets with numeric addresses, run:

Terminal
sudo netstat -ant

The State column tells you where each connection is in the TCP lifecycle:

output
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 192.168.1.10:22 192.168.1.5:52710 ESTABLISHED
tcp 0 0 192.168.1.10:80 203.0.113.25:51422 TIME_WAIT
tcp 0 0 127.0.0.1:3306 127.0.0.1:41834 CLOSE_WAIT

ESTABLISHED means the connection is active. TIME_WAIT is normal after a connection closes. A large number of CLOSE_WAIT entries can mean the local application is not closing sockets correctly.

To show only established TCP connections, filter the output:

Terminal
sudo netstat -ant | grep ESTABLISHED

For new troubleshooting work, ss gives better built-in state filters, such as ss -tn state established.

Count Connections

On busy web servers, you may want a quick count instead of a full connection table. To count all current TCP connections that involve port 80, run:

Terminal
sudo netstat -ant | grep ':80' | wc -l

To count only established connections on port 80, add the TCP state to the filter:

Terminal
sudo netstat -ant | grep ':80' | grep ESTABLISHED | wc -l

These counts are useful as a quick signal, but they are not a replacement for application metrics. The number can change while the pipeline is running, especially on high-traffic systems.

Display the Routing Table

To print the kernel IP routing table, use the -r option:

Terminal
netstat -rn
output
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

The first row is the default route, which sends all traffic not matching another rule to 192.168.1.1. The ip route command from ip is the modern equivalent and is what you should use on new systems.

Interface Statistics

The -i option prints a per-interface summary that includes received and transmitted packets, errors, drops, and the MTU:

Terminal
netstat -i
output
Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP TX-OK TX-ERR TX-DRP Flg
eth0 1500 2345678 0 12 1987654 0 0 BMRU
lo 65536 55432 0 0 55432 0 0 LRU

A growing RX-ERR or TX-ERR column is a hint that you have a cabling, duplex, or driver issue on that interface.

Protocol Statistics

To print a summary of statistics for each protocol, use -s:

Terminal
netstat -s

The output is long and grouped by protocol (Ip, Tcp, Udp, and so on). It includes counters such as total packets received, segments retransmitted, and connection resets. You can narrow the output to a single protocol by combining -s with -t or -u:

Terminal
netstat -st

netstat vs ss

On modern Linux distributions, ss is the recommended replacement for netstat. It is faster and reads information directly from the kernel through Netlink, with richer filter support. The ip command replaces the routing and interface parts of netstat.

A few common translations:

  • netstat -tuln is equivalent to ss -tuln
  • netstat -tulnp is equivalent to sudo ss -tulnp
  • netstat -s maps to ss -s (a much shorter summary)
  • netstat -i maps to ip -s link
  • netstat -r maps to ip route

For a full walkthrough of the replacement tool, see the guide on the ss command .

Quick Reference

For a printable quick reference, see the netstat cheatsheet .

Command Description
netstat Show active non-listening sockets
sudo netstat -a Show all listening and non-listening sockets
sudo netstat -at Show all TCP sockets
sudo netstat -au Show all UDP sockets
sudo netstat -tuln Show TCP and UDP listening sockets with numeric output
sudo netstat -tulnp Show listening sockets with PID and process name
sudo netstat -tulnp | grep ':80' Find the process listening on port 80
sudo netstat -ant Show all TCP sockets with numeric addresses
sudo netstat -ant | grep ESTABLISHED Show established TCP connections
sudo netstat -ant | grep ':80' | wc -l Count TCP connections involving port 80
sudo netstat -atnc Refresh TCP connection output every second
netstat -rn Show the routing table with numeric addresses
netstat -i Show interface statistics
netstat -s Show protocol statistics
netstat -st Show TCP protocol statistics

Troubleshooting

netstat: command not found
The net-tools package is not installed. Install it with your package manager, or use ss instead.

No PID/Program name column shown
You need to run the command with sudo. Without root privileges, netstat cannot read process information for sockets owned by other users.

Output is slow or hangs
DNS resolution is slow or failing. Add the -n option to disable name lookups and print numeric addresses and ports.

A port shows up as LISTEN on :: but not on 0.0.0.0
The service is listening on the IPv6 wildcard address. On systems with dual-stack enabled, this accepts IPv4 traffic too. Check your IPv6 configuration if only IPv4 clients are failing.

FAQ

Is netstat deprecated?
Yes. The net-tools suite, which includes netstat, ifconfig, and route, has been deprecated for years in favor of the iproute2 tools ss and ip. It is still functional and still shipped by most distributions, but new scripts should target ss and ip.

What is the difference between netstat on Linux and Windows?
The command name is the same, and the general idea is the same, but the flags are different. This guide covers the Linux version from net-tools. On Windows, the closest equivalents to netstat -tulnp are netstat -ano and Get-NetTCPConnection in PowerShell.

How do I find what process is using a specific port?
Run sudo netstat -tulnp | grep ':PORT', replacing PORT with the port number. The last column shows the PID and program name. With ss, the same query is sudo ss -tulnp 'sport = :PORT'.

Why is a port still TIME_WAIT after I stopped the service?
TIME_WAIT is a normal TCP state that keeps a closed connection around for a short period so late packets can be handled safely. The kernel clears these entries automatically, usually within one or two minutes.

Conclusion

netstat remains useful when you need to inspect network connections on systems that still have net-tools installed. For new scripts and current Linux systems, prefer ss for sockets and ip for routes and interface statistics.

Netcat Cheatsheet

Basic Syntax

Core nc command forms.

Command Description
nc host port Open a TCP connection
nc -u host port Open a UDP connection
nc -l port Listen on a local port
nc -h Show help and options
man nc Read the local Netcat manual

Connect and Listen

Create simple client and server connections.

Command Description
nc example.com 80 Connect to TCP port 80
nc -l 5555 Listen on port 5555
nc server.example.com 5555 Connect to a listening host
nc -v host 22 Connect with verbose output
nc -n 192.168.1.10 22 Skip DNS lookups

Port Checks

Check whether TCP ports are open.

Command Description
nc -z -v host 22 Check one TCP port
nc -z -v host 20-80 Check a port range
nc -z -v host 80 443 Check selected ports
nc -z -w 3 host 443 Check with a timeout
nmap host Use Nmap for deeper scans

UDP

Use UDP instead of TCP.

Command Description
nc -u host 53 Connect to a UDP service
nc -u -l 5555 Listen for UDP datagrams
nc -z -v -u host 53 Check a UDP port
nc -u -w 3 host 123 UDP check with timeout
nc -u 192.168.1.10 5555 Send text to a UDP listener

File Transfers

Send files between two hosts.

Command Description
nc -l 5555 > file Receive a file
nc host 5555 < file Send a file
nc -l 5555 | tar xzvf - Receive and extract a directory
tar czvf - dir | nc host 5555 Archive and send a directory
nc -w 5 host 5555 < file Send with a timeout

HTTP and Raw Requests

Send plain text requests to network services.

Command Description
printf "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" | nc example.com 80 Send an HTTP request
echo "QUIT" | nc mail.example.com 25 Send a simple SMTP command
echo "PING" | nc host 5555 Send text to a listener
nc host 80 Type a request manually
curl http://example.com Use curl for HTTP work

Timeouts and Persistent Listeners

Control how long Netcat waits and whether it keeps listening.

Command Description
nc -w 5 host 80 Timeout after 5 seconds
nc -k -l 5555 Keep listening after disconnect
nc -v -w 3 host 22 Verbose connection with timeout
nc -l 8080 < index.html Serve one file on port 8080
while true; do nc -l 8080 < index.html; done Serve the file repeatedly

Troubleshooting

Quick checks for common Netcat issues.

Issue Check
nc: command not found Install netcat-openbsd, netcat-traditional, or nmap-ncat
Connection refused Confirm the remote service is running and listening on that port
Command hangs Add -w to set a timeout
UDP result is unclear UDP has no TCP-style handshake, use Nmap for reliable scans
Option behaves differently Run man nc and check the Netcat implementation on that system

Related Guides

Use these guides for broader networking workflows.

Guide Description
Netcat Command in Linux Full Netcat guide with examples
Nmap Command in Linux Scan hosts and ports in more detail
curl cheatsheet Work with HTTP requests and APIs
tcpdump cheatsheet Capture and inspect network packets
ss Command in Linux Inspect sockets and listening services

Things to Do After Installing Ubuntu 26.04

After installing Ubuntu 26.04, the desktop is ready to use, but a few setup steps make the system easier to work with day to day. Updating packages, checking drivers, installing media codecs, setting up backups, and reviewing privacy settings are good first tasks on a new installation.

This guide explains what to do after installing Ubuntu 26.04. The checklist is aimed at desktop users who want a clean, practical setup without adding unnecessary tools.

If you have not installed the system yet, start with our guide on how to install Ubuntu 26.04 . If you moved from an older release, see how to upgrade to Ubuntu 26.04 .

Quick Reference

Task Command or Location
Update package lists sudo apt update
Upgrade installed packages sudo apt upgrade
Install media codecs sudo apt install ubuntu-restricted-extras
Install Flatpak support sudo apt install flatpak gnome-software-plugin-flatpak
Add Flathub flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
Install GNOME Tweaks sudo apt install gnome-tweaks
Enable firewall sudo ufw enable
Check firewall status sudo ufw status
Remove unused packages sudo apt autoremove
Check Ubuntu version lsb_release -a
Review privacy settings Settings > Privacy & Security
Set up backups Settings > System > Backup

Update Ubuntu

Start by updating the package index and installing available updates. This pulls in security fixes, bug fixes, and package updates released after the installation image was created.

Open a terminal and run:

Terminal
sudo apt update
sudo apt upgrade

Enter your password when prompted and confirm the upgrade. If the update installs a new kernel or graphics driver, reboot before continuing:

Terminal
sudo reboot

You can also update from the desktop by opening Software Updater from the application menu.

Install Available Drivers

Ubuntu detects most hardware automatically, but some systems need proprietary drivers for graphics cards, Wi-Fi adapters, or other devices. This is common on laptops and systems with NVIDIA graphics.

Open Software & Updates, go to the Additional Drivers tab, and wait while Ubuntu checks available drivers. If Ubuntu recommends a proprietary driver, select it and apply the change. Reboot after installing graphics or Wi-Fi drivers.

If you selected the third-party software option during installation, your system may already have the correct driver installed. It is still worth checking this screen once on a new system.

Install Media Codecs

Ubuntu does not include every media codec by default. If you want better support for common audio and video files, Microsoft fonts, and some archive formats, install the restricted extras package:

Terminal
sudo apt install ubuntu-restricted-extras

During installation, you may be asked to accept the Microsoft font license. Use the keyboard to select OK, press Enter, then select Yes and press Enter again.

After the package is installed, try playing the video or audio file again. For most desktop users, this package is enough for everyday media playback.

Enable Flatpak and Flathub

Ubuntu includes Snap support by default, and many applications are available from the Ubuntu App Center. Flatpak is another popular desktop app format, and Flathub provides current builds of many Linux desktop applications.

Install Flatpak and the GNOME Software plugin:

Terminal
sudo apt install flatpak gnome-software-plugin-flatpak

Add the Flathub repository:

Terminal
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

Log out and log back in, or reboot, so desktop app integration is fully available:

Terminal
sudo reboot

After that, you can install Flatpak applications from Flathub or from GNOME Software if the plugin is active.

Install Common Desktop Apps

A fresh Ubuntu installation includes a browser, file manager, terminal, text editor, and basic utilities. Most users still add a few daily-use applications.

Common choices include:

  • A web browser such as Google Chrome , Chromium, Microsoft Edge, or Firefox.
  • A media player such as VLC.
  • Development tools such as Node.js , Git, Python, Docker, or Visual Studio Code.
  • Communication apps such as Slack, Discord, Zoom, or Teams.
  • Desktop apps from Flathub when the Ubuntu repositories or Snap packages do not provide the version you want.

Use the Ubuntu App Center for simple desktop installs. Use apt when a package is available in Ubuntu repositories, and use the vendor’s official package only when you need the current upstream version.

Set Up Backups

Set up backups before you spend too much time customizing the system. Backups are easier to trust when they are configured early and tested before you need them.

Open Settings > System > Backup and choose where to store your backups. An external drive is the simplest option for a single desktop or laptop. If you already use a cloud storage provider or network storage, configure that target instead.

At minimum, back up your home directory, which contains documents, downloads, browser profiles, SSH keys, and most application settings. If you keep important files outside your home directory, include those paths too.

After the first backup finishes, restore one small test file to confirm that the backup is usable.

Review Privacy and Security Settings

Open Settings > Privacy & Security and review the options that match how you use the computer. On a laptop, pay special attention to location services, screen lock, automatic suspend, and notifications on the lock screen.

Good defaults for most systems are:

  • Disable location services unless you use apps that need location access.
  • Keep the screen lock enabled.
  • Use a short automatic blank-screen delay on laptops.
  • Hide notification details on the lock screen if the computer is used in public places.
  • Review diagnostics and error reporting options.

If the computer is shared, create separate user accounts instead of sharing one login. Use an administrator account only when needed, and keep daily users separate when possible.

Enable the Firewall

Ubuntu includes UFW, a simple firewall tool for managing incoming connections. On a typical desktop system, enabling UFW blocks unsolicited inbound traffic while allowing normal outbound connections.

Enable the firewall:

Terminal
sudo ufw enable

Check the status:

Terminal
sudo ufw status
output
Status: active

If you use SSH, file sharing, development servers, or remote desktop tools, allow those services before relying on the firewall rules. For a full command reference, see our UFW firewall guide .

Customize the Desktop

Ubuntu 26.04 uses the GNOME desktop with Ubuntu’s dock and appearance settings. You can adjust the most common options from Settings > Appearance.

Useful settings to review include:

  • Light or dark style.
  • Accent color.
  • Dock position and auto-hide behavior.
  • Desktop icons.
  • Default applications.
  • Keyboard shortcuts.
  • Display scaling for high-resolution screens.

Do not change every option at once. Make a few changes, use the desktop for a day, then adjust anything that still feels awkward.

Install GNOME Tweaks

GNOME Tweaks exposes settings that are not shown in the default Settings app. It is useful for changing window behavior, fonts, startup applications, and a few interface details.

Install it with:

Terminal
sudo apt install gnome-tweaks

Open Tweaks from the application menu. If you also use GNOME Shell extensions, install only the extensions you need and remove ones you no longer use. Too many extensions can make desktop upgrades harder to troubleshoot.

Improve Laptop Battery Life

On laptops, start with the built-in power settings. Open Settings > Power and choose the profile that matches your current use:

  • Power Saver for longer battery life.
  • Balanced for normal use.
  • Performance when you need maximum speed and the laptop is plugged in.

Also review screen brightness, automatic suspend, Bluetooth, and keyboard backlight settings. Small changes here often matter more than installing extra tools.

If you need more control, you can install TLP:

Terminal
sudo apt install tlp

Start and enable the service:

Terminal
sudo systemctl enable --now tlp

Use TLP only if you are comfortable troubleshooting power behavior. On many newer laptops, Ubuntu’s built-in power profiles are enough.

Remove Unused Packages

After installing updates and applications, remove packages that were installed as dependencies but are no longer needed:

Terminal
sudo apt autoremove

Review the package list before confirming. autoremove is usually safe, but it is still a good habit to read what will be removed.

You can also clear downloaded package files from the local cache:

Terminal
sudo apt clean

This frees disk space, but it means packages will need to be downloaded again if you reinstall them later.

Check the Ubuntu Version

After updates and any reboot, confirm that the system is running Ubuntu 26.04:

Terminal
lsb_release -a
output
Distributor ID: Ubuntu
Description: Ubuntu 26.04 LTS
Release: 26.04
Codename: resolute

If lsb_release is not installed, you can check /etc/os-release:

Terminal
cat /etc/os-release

For more options, see how to check your Ubuntu version .

Next Steps

Once the desktop basics are in place, install the tools you need for your work. Developers may want Docker , Node.js, Git, Python, or Visual Studio Code. Desktop users may want Chrome, VLC, Flatpak apps, printer support, or cloud sync tools.

If you plan to use the machine as a server, create a separate setup plan. Desktop post-install tasks are different from server hardening, SSH access, service configuration, and firewall rules for hosted applications.

Conclusion

Ubuntu 26.04 works well after a clean installation, but updates, drivers, codecs, backups, firewall settings, and desktop preferences make the system more practical for daily use. Start with the essentials, then add only the applications and customizations you actually need.

How to Install Ubuntu 26.04

Installing Ubuntu 26.04 gives you a fresh long-term support desktop with the current Ubuntu installer, GNOME desktop, and updated system packages. A clean installation is the right choice when you are setting up a new computer, replacing another operating system, or starting over with a known-good system.

This guide explains how to install Ubuntu 26.04 from a bootable USB drive. We will download the ISO, create the installer USB, boot from it, walk through the installer screens, and review the first steps after the system starts.

Prerequisites

Before you start, make sure you have:

  • A computer where you want to install Ubuntu 26.04.
  • A USB flash drive with at least 12 GB of storage.
  • A reliable internet connection.
  • A backup of any files you want to keep from the target computer.

Installing Ubuntu can erase the selected disk. If the computer already contains another operating system or personal files, back up your data before continuing.

If you already run an older Ubuntu release and want to keep your existing setup, see how to upgrade to Ubuntu 26.04 instead of doing a clean install.

Download the Ubuntu 26.04 ISO

Download the Ubuntu 26.04 Desktop ISO from the official Ubuntu downloads page . Choose the 64-bit desktop image and save it to your computer. The file name should look similar to ubuntu-26.04-desktop-amd64.iso.

If Ubuntu publishes checksum files for the release, verify the ISO before writing it to the USB drive. This step confirms that the file downloaded correctly and was not corrupted.

Create a Bootable Ubuntu USB Drive

Write the ISO file to a USB flash drive with a tool such as Rufus, balenaEtcher, GNOME Disks, or Startup Disk Creator. The exact steps depend on your current operating system, but the process is the same:

  1. Select the Ubuntu 26.04 ISO file.
  2. Select the USB flash drive.
  3. Start the write process.
  4. Wait until the tool finishes and safely ejects the drive.
Warning
Writing the ISO to a USB drive erases the selected drive. Double-check that you selected the correct USB device before starting.

Boot From the USB Drive

Insert the USB drive into the computer where you want to install Ubuntu and restart the machine. Open the boot menu during startup and select the USB drive.

The key used to open the boot menu depends on the computer manufacturer. Common keys include F12, F10, F9, Esc, and Del. If the computer starts the existing operating system instead, restart and try the boot-menu key again.

When the Ubuntu boot menu appears, choose Try or Install Ubuntu.

Ubuntu 26.04 boot menu with Try or Install Ubuntu selected

Choose Language and Accessibility Options

The installer starts with the language screen. Select your preferred language and click Next.

Ubuntu 26.04 installer language selection screen

The next screen lets you configure accessibility options before installation. Most users can leave these settings unchanged and continue.

Ubuntu 26.04 installer accessibility options screen

Select Keyboard Layout and Network

Choose your keyboard layout. If you are unsure, use the text field on the screen to test keys such as quotes, symbols, and special characters.

Ubuntu 26.04 installer keyboard layout screen

Next, connect to the internet if the installer asks for network access. A wired Ethernet connection is usually detected automatically. For Wi-Fi, select your network and enter the password.

Ubuntu 26.04 installer network connection screen

You can install Ubuntu without internet access, but connecting during installation allows the installer to download updates and third-party packages when those options are selected.

Choose the Installation Type

When asked what you want to do, select Install Ubuntu.

Ubuntu 26.04 installer screen for choosing to install Ubuntu

The Try Ubuntu option starts a live desktop without changing the disk. Use it if you want to test hardware support before installing.

Choose Interactive Installation

On the next screen, choose Interactive installation. This is the standard installer path for a single desktop computer.

Ubuntu 26.04 installer interactive installation screen

The automated installation option is for advanced deployments where you provide an installation configuration file. Most desktop users should leave it unselected.

Select Apps and Third-Party Software

Choose the application set you want to install. The default selection is a good fit for most desktop systems. The extended selection installs more applications during setup.

Ubuntu 26.04 installer apps selection screen

Ubuntu 26.04 uses Default selection for a smaller desktop setup and Extended selection for additional tools such as office utilities. Choose the default option if you want a clean desktop and plan to add applications later. Choose the extended option if you want more applications installed immediately.

The next screen offers proprietary software for graphics and Wi-Fi hardware, along with support for additional media formats.

Ubuntu 26.04 installer third-party software screen

Enable these options when you have a working internet connection. They can help with NVIDIA graphics, some Wi-Fi adapters, and common media playback.

Choose Disk Setup

The disk setup screen is the most important part of the installation.

For a clean install on a dedicated computer or virtual machine, choose the option to erase the disk and install Ubuntu. This creates the required partitions automatically.

Ubuntu 26.04 installer disk setup screen

If you are installing Ubuntu next to another operating system, read the installer options carefully before continuing. Do not erase the disk unless you want to remove the existing operating system and all files on that disk.

Advanced users can choose manual partitioning to control mount points, file systems, and encryption. For most desktop installs, the automatic disk setup is simpler and less error-prone.

Choose Encryption and File System Options

After choosing the disk setup, select the encryption and file system options. For a basic desktop installation, leave No encryption selected.

Ubuntu 26.04 installer encryption and file system screen

If you are installing Ubuntu on a laptop or a computer that stores private data, disk encryption is worth considering. Make sure you can store the recovery key safely, because encrypted data is difficult or impossible to recover without the correct passphrase or recovery key.

Create Your User Account

Enter your name, computer name, username, and password. Choose a strong password because this account will be used for desktop login and administrative tasks with sudo .

Ubuntu 26.04 installer user account creation screen

You can choose automatic login if the computer is for personal use in a trusted location. For laptops, shared machines, and work systems, require a password at login.

Select Time Zone

Select your time zone on the map or search for your city. The installer uses this setting to configure the system clock.

Ubuntu 26.04 installer time zone selection screen

If you are connected to the internet, Ubuntu can usually detect the correct time zone automatically.

Review and Start the Installation

Before copying files, the installer shows a summary of the selected options. Review the disk, keyboard layout, time zone, and account details.

Ubuntu 26.04 installer ready to install summary screen

When everything looks correct, start the installation. Ubuntu will copy files to the disk, install packages, configure the boot loader, and prepare the system for first boot.

Ubuntu 26.04 installation progress screen

The installation can take several minutes depending on your hardware and USB drive speed.

Restart Into Ubuntu 26.04

When the installer finishes, restart the computer and remove the USB drive when prompted.

Ubuntu 26.04 installer restart prompt

After the reboot, the computer should start from the internal disk and show the Ubuntu login screen or desktop.

Ubuntu 26.04 desktop after installation

Ubuntu may show a short welcome wizard after the first login. Use it to review location services, privacy reporting, appearance, and application suggestions, or skip the options you do not need.

First Steps After Installing Ubuntu 26.04

After logging in, open a terminal and update the package index:

Terminal
sudo apt update

Install available updates:

Terminal
sudo apt upgrade

You can check your Ubuntu version with:

Terminal
lsb_release -a

If this is a server or a machine you will access remotely, consider setting up SSH and a firewall. See our guides on enabling SSH on Ubuntu and setting up UFW on Ubuntu .

You may also want to install extra .deb packages for applications that are not available from the Ubuntu repositories. For details, see how to install deb files on Ubuntu .

For a typical desktop setup, you can install Google Chrome on Ubuntu 26.04 and, if you do development work, Docker on Ubuntu 26.04 .

Troubleshooting

The computer does not boot from the USB drive
Open the boot menu and choose the USB device manually. If the USB drive is not listed, recreate it with another tool or try a different USB port.

The installer freezes or shows a blank screen
Restart and choose the safe graphics option from the Ubuntu boot menu. This can help on systems with graphics drivers that do not work well during installation.

The disk you want to use is not listed
Check the computer firmware settings for storage mode and disk controller options. On some systems, switching from RAID or Intel RST mode to AHCI is required before Linux installers can detect the disk. Back up data before changing storage settings.

Wi-Fi does not work during installation
Install without network access and connect after the first boot. If the Wi-Fi adapter needs third-party drivers, connect with Ethernet or USB tethering, then install updates and additional drivers from Ubuntu.

The system boots back into the installer after installation
Remove the USB drive and reboot. If it still opens the installer, check the boot order in the firmware settings and move the internal disk above the USB device.

FAQ

Can I install Ubuntu 26.04 without internet access?
Yes. You can install Ubuntu without an internet connection. Updates, language packs, and some third-party packages can be installed after the first boot.

Will installing Ubuntu erase Windows?
It depends on the disk option you choose. Erasing the disk removes Windows and all files on that disk. If you want to keep Windows, choose the install-alongside option when available or use manual partitioning.

How much disk space does Ubuntu 26.04 need?
Ubuntu can install on a modest disk, but a desktop system is more comfortable with at least 25 GB of free space. Use more if you plan to install many applications, keep large files, or run development tools.

Should I choose default or extended installation?
Choose the default selection if you want a smaller desktop setup and plan to install applications as needed. Choose the extended selection if you want more desktop tools installed immediately.

Conclusion

You now have Ubuntu 26.04 installed and ready to use. After the first login, install updates, confirm that your hardware works, and add only the applications and services you need for your setup.

How to Install VirtualBox on Ubuntu 26.04

VirtualBox is a desktop virtualization application that lets you run Linux, Windows, BSD, and other guest operating systems in virtual machines. It is useful for testing software, running lab environments, and keeping experiments separate from your main system.

This guide explains how to install VirtualBox on Ubuntu 26.04 from the Ubuntu multiverse repository.

At publication time, Ubuntu 26.04 packages VirtualBox 7.2 in the Ubuntu repositories. The Oracle VirtualBox APT repository does not yet provide a dedicated resolute repository suite, so the Ubuntu package is the safer install path for this release.

Quick Reference

Task Command
Enable multiverse sudo add-apt-repository multiverse
Install VirtualBox sudo apt install virtualbox
Install Extension Pack sudo apt install virtualbox-ext-pack
Check version VBoxManage --version
Launch VirtualBox virtualbox
Remove VirtualBox sudo apt remove virtualbox virtualbox-ext-pack

Prerequisites

You need to be logged in as root or as a user with sudo privileges .

If Secure Boot is enabled, Ubuntu may ask you to enroll a Machine Owner Key (MOK) so the VirtualBox kernel modules can load. Follow the on-screen prompts and reboot when asked.

Installing VirtualBox from Ubuntu Repositories

First, update the package index:

Terminal
sudo apt update

Enable the multiverse repository if it is not already enabled:

Terminal
sudo add-apt-repository multiverse

Refresh the package index again:

Terminal
sudo apt update

Install VirtualBox:

Terminal
sudo apt install virtualbox

After the installation finishes, check the installed version:

Terminal
VBoxManage --version

The output prints the installed VirtualBox version:

output
7.2.6_Ubuntur172322

Installing the VirtualBox Extension Pack

The VirtualBox Extension Pack adds support for features such as USB 2.0 and USB 3.0 devices, webcam passthrough, disk encryption, and remote display access.

Install the Ubuntu package:

Terminal
sudo apt install virtualbox-ext-pack

The installer downloads the matching Oracle Extension Pack and asks you to accept the license terms. Read the prompt, then select the confirmation option if you agree.

Verify that the Extension Pack is installed:

Terminal
VBoxManage list extpacks

Starting VirtualBox

Open the Activities overview, search for “VirtualBox”, and start it from the application menu.

You can also start VirtualBox from the terminal:

Terminal
virtualbox

When VirtualBox opens, click “New” to create your first virtual machine:

VirtualBox main window on Ubuntu 26.04

Uninstalling VirtualBox

To remove VirtualBox and the Extension Pack:

Terminal
sudo apt remove virtualbox virtualbox-ext-pack

Remove packages that are no longer needed:

Terminal
sudo apt autoremove

If you want to remove saved virtual machines, delete them from the VirtualBox interface first or remove the files manually from your home directory. Do this only after you have backed up any virtual machines you still need.

Troubleshooting

VirtualBox kernel modules do not load
Install the headers for your running kernel and reconfigure VirtualBox:

Terminal
sudo apt install linux-headers-$(uname -r)
sudo dpkg-reconfigure virtualbox-dkms

Secure Boot blocks VirtualBox modules
Enroll the MOK key when Ubuntu prompts you, then reboot. If you skipped the enrollment screen, reinstall or reconfigure the VirtualBox DKMS package so Ubuntu shows the prompt again.

The virtualbox-ext-pack install stops at a license prompt
Use the keyboard to select the confirmation option in the terminal dialog. If the terminal dialog is hard to read, expand the terminal window and run the install command again.

USB devices do not appear in guest machines
Install the Extension Pack, add your user to the vboxusers group, and log out and back in:

Terminal
sudo usermod -aG vboxusers $USER

FAQ

Should I install VirtualBox from Oracle or Ubuntu repositories?
For Ubuntu 26.04, the Ubuntu repository package is the best default choice because it matches the release and kernel packaging. Use the Oracle repository only after Oracle publishes a repository suite for Ubuntu 26.04.

Do I need the Extension Pack?
You only need it for extra features such as USB 2.0/3.0 passthrough and remote display support. Basic virtual machines work without it.

Where are VirtualBox virtual machines stored?
By default, VirtualBox stores virtual machines under ~/VirtualBox VMs/.

Conclusion

VirtualBox is available on Ubuntu 26.04 from the multiverse repository. After installation, add the Extension Pack only if you need its extra device and remote display features.

How to Install TeamViewer on Ubuntu 26.04

TeamViewer is a remote access and support application that lets you connect to another computer over the internet. It supports remote control, file transfer, screen sharing, and unattended access across Linux, Windows, and macOS.

This guide explains how to install TeamViewer on Ubuntu 26.04 using the official .deb package.

Quick Reference

Task Command
Download package wget https://download.teamviewer.com/download/linux/teamviewer_amd64.deb
Install package sudo apt install ./teamviewer_amd64.deb
Launch TeamViewer teamviewer
Check service systemctl status teamviewerd
Update TeamViewer sudo apt update && sudo apt upgrade teamviewer
Remove TeamViewer sudo apt remove teamviewer

Prerequisites

You need to be logged in as root or as a user with sudo privileges .

The command below downloads the x86-64 package. If you are using an ARM device, choose the correct Ubuntu/Debian package from the TeamViewer Linux download page .

Installing TeamViewer on Ubuntu 26.04

TeamViewer is proprietary software and is not available in the standard Ubuntu repositories. Download the official package with wget :

Terminal
wget https://download.teamviewer.com/download/linux/teamviewer_amd64.deb

Install the package with apt:

Terminal
sudo apt install ./teamviewer_amd64.deb

When prompted, type Y and press Enter.

During installation, the TeamViewer APT repository is added to your system. This allows TeamViewer to update through the same package manager you use for other Ubuntu packages.

Starting TeamViewer

Open the Activities overview, search for “TeamViewer”, and click the application icon.

You can also start TeamViewer from the command line:

Terminal
teamviewer

The first time TeamViewer starts, accept the license agreement. After that, the main TeamViewer window displays your ID and password. Share those values only with someone you trust, or enter a remote computer’s ID to start an outgoing connection.

TeamViewer main window on Ubuntu 26.04

Updating TeamViewer

Check the repository file with cat :

Terminal
cat /etc/apt/sources.list.d/teamviewer.list

The file should contain the TeamViewer stable repository:

output
deb https://linux.teamviewer.com/deb stable main

Update TeamViewer with the package manager:

Terminal
sudo apt update
sudo apt upgrade teamviewer

You can also install updates through Ubuntu’s graphical Software Updater.

Uninstalling TeamViewer

To remove TeamViewer while keeping its configuration files:

Terminal
sudo apt remove teamviewer

To remove TeamViewer and its configuration files:

Terminal
sudo apt purge teamviewer

If you also want to remove the TeamViewer repository:

Terminal
sudo rm /etc/apt/sources.list.d/teamviewer.list
sudo apt update

Troubleshooting

teamviewer: command not found after installation
Close and reopen your terminal, then try again. If the command is still missing, verify the package installation:

Terminal
dpkg -l teamviewer

The installation fails because of missing dependencies
Ask apt to repair broken dependencies and retry the package install:

Terminal
sudo apt --fix-broken install
sudo apt install ./teamviewer_amd64.deb

TeamViewer shows “Not ready. Please check your connection.”
Restart the TeamViewer daemon:

Terminal
sudo systemctl restart teamviewerd

Check that the service is running:

Terminal
systemctl status teamviewerd

You are using an ARM system
Do not install the teamviewer_amd64.deb package on ARM. Download the ARM package from TeamViewer’s Linux download page instead.

FAQ

Is TeamViewer free on Ubuntu?
TeamViewer is free for personal, non-commercial use. Commercial use requires a paid license.

Can I install TeamViewer Host instead?
Yes. TeamViewer Host is meant for unattended access. Download the Host package from TeamViewer’s Linux download page and install it with apt the same way.

How do I connect to another computer?
Open TeamViewer, enter the remote computer’s ID in the partner ID field, and start the connection. Enter the remote password when prompted.

Conclusion

TeamViewer installs on Ubuntu 26.04 from the official .deb package. The package also configures the TeamViewer repository so future updates are handled through Ubuntu’s package manager.

How to Install Google Chrome Web Browser on Ubuntu 26.04

Google Chrome is a fast web browser with built-in Google account sync, automatic updates, password management, and support for modern web applications. It is not included in the standard Ubuntu repositories because it is proprietary software.

This guide explains how to install Google Chrome on Ubuntu 26.04 using the official Google .deb package.

Info
The official Google Chrome .deb package is available for 64-bit x86 systems. On ARM devices, use Chromium or another ARM-compatible browser.

Quick Reference

Task Command
Download package wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
Install package sudo apt install ./google-chrome-stable_current_amd64.deb
Start Chrome google-chrome
Set as default browser xdg-settings set default-web-browser google-chrome.desktop
Update Chrome sudo apt update && sudo apt upgrade
Uninstall Chrome sudo apt remove google-chrome-stable
Check repository file cat /etc/apt/sources.list.d/google-chrome.list

Installing Google Chrome on Ubuntu

The official Chrome package installs the browser and configures the Google APT repository so future updates arrive through the normal Ubuntu update process.

Download Google Chrome

Open a terminal and use wget to download the latest stable package:

Terminal
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb

Install Google Chrome

Install the package with apt. This command requires sudo privileges :

Terminal
sudo apt install ./google-chrome-stable_current_amd64.deb

When prompted, enter your password and confirm the installation.

Starting Google Chrome

Open the Activities overview, search for “Google Chrome”, and launch it:

Open Google Chrome on Ubuntu 26.04

You can also start Chrome from the terminal:

Terminal
google-chrome

When Chrome starts for the first time, it asks whether you want to set it as the default browser and send crash reports:

Google Chrome default browser prompt on Ubuntu 26.04

Chrome then opens the welcome page:

Google Chrome welcome page on Ubuntu 26.04

From here, you can sign in with your Google account and sync bookmarks, history, passwords, and extensions.

Set Chrome as Default Browser

To set Chrome as the default browser from the command line, run:

Terminal
xdg-settings set default-web-browser google-chrome.desktop

Verify the current default browser:

Terminal
xdg-settings get default-web-browser
output
google-chrome.desktop

Updating Google Chrome

The Chrome package adds the Google repository to your system. Check the repository file with cat :

Terminal
cat /etc/apt/sources.list.d/google-chrome.list

The file should contain a Google Chrome repository entry for the stable channel.

Chrome updates are installed through the standard Ubuntu update workflow:

Terminal
sudo apt update
sudo apt upgrade

Uninstalling Google Chrome

To remove Google Chrome, run:

Terminal
sudo apt remove google-chrome-stable

Remove packages that are no longer needed:

Terminal
sudo apt autoremove

If you also want to remove the Google Chrome repository file, delete it and update the package index:

Terminal
sudo rm /etc/apt/sources.list.d/google-chrome.list
sudo apt update

Troubleshooting

The installation fails with dependency errors
Fix broken dependencies and repeat the installation:

Terminal
sudo apt --fix-broken install
sudo apt install ./google-chrome-stable_current_amd64.deb

Chrome does not start after installation
Close any running Chrome processes and start it again:

Terminal
pkill -f google-chrome
google-chrome

The repository file is missing
Reinstall the downloaded package so the Chrome repository is recreated:

Terminal
sudo apt install ./google-chrome-stable_current_amd64.deb

FAQ

What is the difference between Chrome and Chromium?
Chromium is the open-source browser project that Chrome is based on. Chrome adds proprietary media codecs, Google account integration, and Google-managed update packaging.

Can I install Chrome Beta or Dev on Ubuntu?
Yes. Download the Beta or Dev .deb package from Google and install it with apt in the same way as the stable package.

Does Chrome update automatically on Ubuntu?
Chrome is updated by Ubuntu’s package manager after the Google repository is added. Run sudo apt update && sudo apt upgrade to install available updates.

Conclusion

Google Chrome installs on Ubuntu 26.04 through the official .deb package. After installation, Chrome receives updates from the Google repository along with your other system packages.

❌