ubuntutorials
Ubuntu 26.04 · 24.04 · 22.04

How to Install Apache2 on Ubuntu

Get Apache serving your own domain over HTTP on a clean Ubuntu server, with the firewall opened correctly and a virtual host that won't fall over the moment you add a second site. Tested on 26.04, 24.04 and 22.04.

Verified
Tested end to end on real servers running Ubuntu 26.04 LTS (kernel 7.0.0-27), 24.04.4 LTS (6.8.0-134) and 22.04.5 LTS (5.15.0-185), today.

Type your own and every command below updates. Nothing leaves your browser.

Apache is the web server I reach for when I want something predictable. It’s in Ubuntu’s default repositories, it starts on boot without being asked twice, and the Debian-style config layout makes a lot of sense once you know which directory does what.

We’ll go from a clean Ubuntu server to Apache serving your own domain, with the firewall configured properly rather than left wide open. The commands are identical on Ubuntu 26.04, 24.04 and 22.04, and all three are tested.

Before You Start

  • A server running Ubuntu 26.04, 24.04 or 22.04, with a user that has sudo access
  • A domain with an A record pointing at your server’s public IP
  • Port 80 reachable from outside, which most cloud firewalls allow by default (do check yours)

If you haven’t pointed a domain at the server yet you can still follow along to the end of step 4, then come back for the virtual host once DNS is sorted.

Step 1: Update the Package Index

sudo apt update

This refreshes the list of available packages so you install the current Apache build rather than whatever your server image happened to ship with. It takes a few seconds on a fresh server.

Step 2: Install Apache

sudo apt install -y apache2

The -y answers the confirmation prompt for you. Ubuntu enables and starts the apache2 service as part of the install, so there’s no separate systemctl enable to run afterwards.

Check what you’ve landed on:

apache2 -v
Server version: Apache/2.4.66 (Ubuntu)
Server built:   2026-07-06T15:33:20

Step 3: Open the Firewall

Ubuntu ships with ufw, and Apache registers a set of application profiles with it during install. You can see them with:

sudo ufw app list
Available applications:
  Apache
  Apache Full
  Apache Secure
  OpenSSH

Apache opens port 80, Apache Secure opens 443, and Apache Full opens both. We’re only doing HTTP here, so Apache is the one you want:

sudo ufw allow "Apache"

Don’t skip this one. If you enable ufw later without an Apache rule already in place, the site goes dark and the cause isn’t obvious from the server’s point of view, since Apache carries on looking perfectly healthy.

Step 4: Check Apache Is Running

systemctl status apache2

You’re looking for Active: active (running) near the top. Press q to get your prompt back.

Now ask the server for its own default page:

curl -s -o /dev/null -w "%{http_code}\n" http://localhost
200

A 200 means Apache is listening and serving. If you get 000 instead, Apache isn’t running at all and step 5 won’t help you, so sort that first.

Step 5: Create a Virtual Host

Apache serves /var/www/html out of the box. That’s fine for a quick test, but a virtual host per site keeps things tidy and turns adding a second domain later into a five minute job.

Create the document root:

sudo mkdir -p /var/www/yourdomain.com/public_html

Hand ownership to your own user so you can deploy files without reaching for sudo every time:

sudo chown -R $USER:$USER /var/www/yourdomain.com/public_html

Drop in a placeholder page so there’s something to look at:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>yourdomain.com</title>
  </head>
  <body>
    <h1>yourdomain.com is live</h1>
  </body>
</html>

Then the virtual host itself:

<VirtualHost *:80>
    ServerName yourdomain.com
    ServerAlias www.yourdomain.com
    DocumentRoot /var/www/yourdomain.com/public_html

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

Enable your site, and disable the default one so it stops catching requests that should be going to yours:

sudo a2ensite yourdomain.com.conf
sudo a2dissite 000-default.conf

Test the configuration before you reload anything. Apache refuses to start on a syntax error, and finding that out now is a good deal more pleasant than finding out during a restart:

sudo apache2ctl configtest
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
Syntax OK

That AH00558 warning turns up even though you’ve set ServerName inside the virtual host, and it’s harmless. There’s a note in the troubleshooting section below if you’d rather silence it.

sudo systemctl reload apache2

reload picks up the new config without dropping existing connections, which is the one to use on a live server. restart also works and is a bigger hammer than you need here.

Verify It Worked

Run this one from your own machine rather than from the server, so you’re testing DNS, the firewall and Apache together rather than just the last of the three:

curl -s http://yourdomain.com
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>yourdomain.com</title>
  </head>
  <body>
    <h1>yourdomain.com is live</h1>
  </body>
</html>

If that returns your page, you have a working Apache install serving your domain. Open it in a browser for good measure.

Troubleshooting

AH00558: apache2: Could not reliably determine the server's fully qualified domain name

You’ll see this from configtest even with ServerName set inside your virtual host, because Apache is complaining about the absence of a global one. It’s a warning rather than an error and Apache runs perfectly well with it. If it bothers you, add a line to /etc/apache2/conf-available/servername.conf with ServerName yourdomain.com and enable it with sudo a2enconf servername.

Job for apache2.service failed after install

Usually something else is already sitting on port 80, and nginx is the usual culprit. Find out who:

sudo ss -tlnp | grep ':80'

Stop the other service, or move Apache to a different port, then start Apache again.

403 Forbidden instead of your page

Apache can read /var/www/yourdomain.com/public_html but can’t traverse the directories above it. Check the permissions:

namei -l /var/www/yourdomain.com/public_html/index.html

Every directory in that chain needs to be executable by www-data. sudo chmod 755 /var/www/yourdomain.com usually settles it.

Conclusion

You’ve got Apache serving a virtual host on port 80, which is the right place to stop before adding TLS. The obvious next step is a certificate, and Let’s Encrypt will pick up the ServerName you’ve just set rather than making you type it again.

If something here doesn’t work on your setup, do drop me a line at hello@ubuntutorials.com with the Ubuntu version you’re on and I’ll get it retested.