Install Apache on Ubuntu 18.04 Bionic Beaver Linux


Learn how install Apache on Ubuntu 18.04, how to configure virtual hosts, setup the firewall and use ssl certificates for a secure connection


  • Root permissions


  • # – requires given linux commands to be executed with root privileges either
    directly as a root user or by use of sudo command
  • $ – requires given linux commands to be executed as a regular non-privileged user


The Apache web server doesn’t need big presentations: open source software, released by the Apache foundation, is one of the most used web servers in the world. In this tutorial, we will see how to install it, adjust the firewall configuration to allow http and https traffic, and setup virtual hosts on Ubuntu 18.04.


Installing the Apache web server on Ubuntu 18.04 Bionic Beaver is a really straightforward process:

$ sudo apt-get update && apt-get install apache2

Ubuntu installation scripts will take care of starting and enabling the apache2 service at boot.

Firewall setup

To access the default content served by Apache, on the same machine the server is running on, we’ll just have to fire up a web browser and navigate to localhost in the address bar. If all is setup correctly, a page should welcome us with the “it works!” message:

Apache welcome page

Apache welcome page

If a firewall is enabled on our system (like it should), to make the content accessible from outside our machine, we need to allow incoming traffic on port 80. The command to run depends on the firewall manager in use. For example, when using ufw (Ubuntu’s default), we must run:

$ sudo ufw allow http

Similarly, if using firewalld, we can run:

$ sudo firewall-cmd --permanent --add-service=http && firewall-cmd --reload

Notice that the above command will have its effect on the default firewalld zone. If we want to operate on another one, we must specify it with the --zone option.

Configuring a virtual host

The apache web server has the ability to run more than one website on the same machine. Each site (a virtual host in the apache terminology) that should be served must have its own configuration. A virtual host can be ip or named based.

In this tutorial we will focus on the second type, since it is the easier to setup and doesn’t require multiple ip addresses (name-based virtual hosts allows many websites to share the same address).

The default virtual host

On Ubuntu, the default virtual host is defined into the /etc/apache2/sites-available directory, inside the 000-default.conf file. Let’s take a look at it:

<VirtualHost *:80>
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/html


	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined


The <VirtualHost> directive on Line 1 is used to group the settings used by apache for a specific virtual host. The first thing we saw defined in it, is the *:80 instruction. This indicates the ip address and port used by the virtual host.

Multiple virtual hosts can be defined in the same file or by following the “one virtual host definition per file” scheme. In both cases the first definition is considered to be the default, if no other virtual host is matched by the client request.

The ServerAdmin directive on Line 3is optional, and it is used to specify the contact address that the web server will show in case of error messages. Normally we want to provide a valid email address as the argument of this directive, since the web server will use mailto: on it, to make contacting the administrator easier.

DocumentRoot on Line 4is mandatory and it’s essential for the virtual host configuration. The argument to this instruction must be a valid filesystem path. The directory provided will be considered the root directory of the virtual host, and must not contain a trailing ‘/’. In this case, the document root directory it’s /var/www/html. If we take a look at its content, we see that it contains the index.html page used as the server welcome page we saw before.

The last two instructions on Line 8 – 9provided in this virtualhost are ErrorLog and CustomLog. By using the first one, we set the file to which the server will log the occurring errors. The second, instead is used to log the requests sent to the server in the specified format (you can use this as a reference for an in-depth knowledge about log formats).

Create a new virtual host

We saw how the default virtualhost is defined; now suppose we want to serve another website using our webserver: we must define a new virtual host to be able to accomplish our goal.

As said above, virtual hosts files must be defined inside the /etc/apache2/sites-available directory (at least in debian-based distributions): therefore we will create our file there. Before doing that, we want to create the directory to be used as our document root, and create a basic page to be displayed when we reach the site:

$ sudo mkdir /var/www/example && echo "Welcome to example!" > /var/www/example/index.html

We can now proceed on configuring our virtual host:

<VirtualHost *:80>
DocumentRoot /var/www/example
ServerName www.example.local

This is the minimal configuration needed to run a virtual host. Here we can see a new directive, ServerName: This is what defines our virtual host. Let’s save this file as example.conf. To activate our virtualhost we use the a2ensite command: all this command does is create a symlink of the file into the /etc/apache2/sites-enabled directory:

$ sudo a2ensite example.conf

After that, we must reload the server configuration:

$ sudo systemctl reload apache2.service

We defined our virtualhost, however since this is a test and we don’t have a dns entry associated with it, to verify that the configuration works we must add an entry in the /etc/hosts file of the machine we are trying to reach the site from.

$ sudo echo " www.example.local" >> /etc/hosts

Without this line, (and without a DNS entry) would be impossible to associate the address of the server to the name of our virtualhost, and using the server ip directly, would instead “trigger” the default virtual host.

From the client machine, if we now navigate to “www.example.local” we should see the minimal page we setup above:

Example Virtualhost Index

Example Virtualhost Index

Setting up ssl

Ssl, short for Secure Sockets Layer it’s the technology which allows us to encrypt the data involved in the connection between the client and the server. When ssl certificates are used, https (Hyper Text Transfer Protocol Secure) replaces http in the url.

Ssl certificates are issued by a certificate authority, which, as a trustful third party, assures that someone is really who claims to be on the internet. Ssl certificates can be very expensive, however there are two main alternatives to obtain a certificate: create a self signed certificate or get one from Let's encrypt.

Generate a self-signed ssl certificate

Although generating a self-signed certificate it’s not a difficult task and can be useful when you just want to achieve encryption, it is not usable in contexts where the certificate itself must be signed by a trusted third party. We can generate a self-signed certificate by using the openssl utility:

$ sudo openssl req -x509 \
  -days 365 \
  -sha256 \
  -newkey rsa:2048 \
  -nodes \
  -keyout example.key \
  -out example-cert.pem

Let’s see what this command does. The first option we encounter, -x509, modifies the behavior of the command so that the it generates a self-signed certificate instead of a certificate request.

With -days, we set the validity, in days, for the certificate. The next provided option is -newkey: with it we create a new key, in this case an rsa key, with the size of 2048 bits. For our test case, we don’t want to encrypt the private key file, so we used -nodes. If this option is omitted, the file in which the key is stored will be protected by a password, which we will be prompted to insert each time the web server is restarted.

With -keyout and -out we specify the file to write the generated key and the certificate, respectively. When launching the command, we will be prompted to answer some questions, and then the key and the certificate will be generated.

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:IT
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:Milan
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Damage Inc.
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:www.example.local
Email Address []:

Next step is to copy our generated key and certificate into /etc/ssl/private and /etc/ssl/ssl-certs directories respectively:

$ sudo mv example-cert.pem /etc/ssl/certs

The certificate is public, so needs no special permission. Now, the key:

$ sudo mv example.key /etc/ssl/private

It’s important that we adjust the key file permissions. If we examine the /etc/ssl/private folder, we can see that it belongs to the root user and the ssl-cert group, and it has 710 as permissions, meaning that while the owner has full privileges on it, the group owner can only access it and list its content, and no permission are allowed for others:

$ ls -ld /etc/ssl/private
drwx--x--- 2 root ssl-cert 4096 Mar 16 11:57 /etc/ssl/private

Let’s change our key file permissions accordingly, giving the owner reading and writing permissions, and read-only privileges for the group:

$ sudo chown root:ssl-cert /etc/ssl/private/example.key
$ sudo chmod 640 /etc/ssl/private/example.key

To use our certificate, we now have to enable the ssl apache module. We do it by using the a2enmod command:

$ sudo a2enmod ssl

We are almost there. Now it’s time to modify our virtual host and set it this way:

<VirtualHost *:443>
  DocumentRoot /var/www/example
  ServerName www.example.local

  # Enable ssl engine
  SSLEngine on

  SSLCertificateFile    /etc/ssl/certs/example-cert.pem
  SSLCertificateKeyFile /etc/ssl/private/example.key

The port 443 on Line 1is the port used for https (in place of port 80 used for http). We also added the SSLEngine on instruction on Line 6, which is pretty self-explanatory.

Finally on Line 8 – 9 we have specified the paths for our certificate and key files, using the SSLCertificateFile and SSLCertificateKeyFile instructions.

Now, follow the instuctions to open firewall ports used at the beginning of the tutorial, but this time to allow the https service:

$ sudo ufw allow https

Finally, reload apache configuration:

$ sudo systemctl reload apache2

All done. Now, if from the client, we navigate to https://www.example.local address, we should see the web server alerting us that the used certificate is not secure (since it’s self trusted). This is, however the sign the our setup works and the traffic between the client and the server will be encrypted (you will need to add an exception for the certificate to use it).

SSL Browser Alert

SSL Browser Alert

Setting up Let’s encrypt

The alternative to commercial and self-signed certificates it’s represented by “Let’s encrypt”. Let’s encrypt is a free, automated and open certificate authority; its goal is to make possible to automatically obtain a certificate trusted by the browser without any human intervention.

This can be achieved by the use of the ACME protocol and a certificate management agent which runs on the server.

To obtain a certificate we must demonstrate we have control over the domain we want to use the certificate for. If we don’t have shell access on the server, we should contact our service provider to activate let’s encrypt on our behalf, but probably there is a dedicated section in the service configuration panel.

If, instead, we do have shell access to the server in question, first of all we must install the certbot ACME client. Installing certbot on Ubuntu 18.04 it’s just matter of running:

$ sudo apt-get update && apt-get install certbot python-certbot-apache

The certbot package comes with a systemd timer unit which will run certbot twice a day to keep the certificate up to date. Obtaining a certificate is quite simple:

$ sudo certbot --apache -m <administrator-email> -d <domain>

Obviously for this to work the domain must point correctly to our publicly accessible server ip. Certbot will prompt you for some questions to tweak the configuration, and if all goes well, the certificate and key should will saved into the /etc/letsencrypt/live/ directory. Just tweak your virtual host file to point to those and you are done!