Ubuntu is a Linux distribution derived from Debian and composed mostly of free and open-source software. Ubuntu is officially released in multiple editions: Desktop, Server, and Core for Internet of things devices and robots.
This details the installation process for Ubuntu as a server on a virtual machine.
Tip
It is recommended to use the minimum requirements for a base VM and expand them as needed for extended VMs.
- CPU:
1
- Memory:
1GB
- Storage:
10GB
-
Go through the installation wizard as per usual.
-
In the Profile setup section:
- Your name: Set a display name suitable for your homelab
- Your server's name: Set a unique name for said server without the domain name (i.e.
ubuntu-server
) - Pick a username: Set a username suitable for your homelab
- Choose a password: Set a secure, minimum 12 character password
- Confirm your password: Confirm the password you have set
-
In the SSH Setup section:
- Install OpenSSH server:
Enabled
- Import SSH identity:
Disabled
- Install OpenSSH server:
-
Complete the installation.
This details some recommended configuration options for Ubuntu as a server.
-
Perform a full system upgrade on the system using
apt
. -
Install the following packages using
apt
:curl
git
htop
nano
net-tools
openssh-server
python3
python3-pip
wget
-
Install packages required by your hypervisor using
apt
:-
ESXi:
open-vm-tools
-
Proxmox:
qemu-guest-agent
[!TIP]
On Proxmox, you may also need to enable theqemu-guest-agent
service yourself.
-
-
Clean up the system using
apt
to recover some storage space. -
Enable the SSH service (if it is not already enabled).
-
Copy over your public SSH key(s) to the system.
-
Decide on a new SSH port (i.e.
2222
) to replace the default port,22
. -
Update the VM's SSH configuration, which includes:
-
Changing the default SSH port to a new port (i.e.
2222
). -
Disabling root login via SSH.
-
Disabling password authentication via SSH.
[!WARNING]
Make sure you have copied over your public SSH key(s) to the system before applying this change!
-
-
Set up the VM firewall (UFW):
-
Check the firewall status to ensure it is active and no rules have been configured.
-
Allow the connection to the new SSH port (i.e.
2222
) using thetcp
protocol.
-
For systems running
cloud-init
(i.e. on a Raspberry Pi), password prompts may not be required when usingsudo
by default.-
To fix this, check the content of the
/etc/sudoers
file:sudo cat /etc/sudoers
-
Identify whether the following line is found in the file and not commented:
%sudo ALL=(ALL:ALL) ALL
If it is not found, add the line to the end of the file accordingly. Likewise, uncomment the line if it is commented.
-
Now, check for a configuration file in the
/etc/sudoers.d/
directory that is responsible for the current behaviour:sudo ls -l /etc/sudoers.d/
Sample output:
total 8 -r--r----- 1 root root 149 Apr 21 2022 90-cloud-init-users -r--r----- 1 root root 958 Feb 3 2020 README
In this example, the configuration file is
90-cloud-init-users
. -
Update the configuration file (i.e.
90-cloud-init-users
) to change this behaviour:sudo nano /etc/sudoers.d/90-cloud-init-users
Sample configuration:
# Created by cloud-init v. 22.2-0ubuntu1~20.04.3 on Thu, 21 Apr 2022 12:54:58 +0000 # User rules for <user> <user> ALL=(ALL) NOPASSWD:ALL
Comment the line with the
NOPASSWD:ALL
option to prevent it from allowing the<user>
user from usingsudo
without a password:# Created by cloud-init v. 22.2-0ubuntu1~20.04.3 on Thu, 21 Apr 2022 12:54:58 +0000 # User rules for <user> - <user> ALL=(ALL) NOPASSWD:ALL + #<user> ALL=(ALL) NOPASSWD:ALL
Alternatively, if you think it is safe, you may also delete the configuration file entirely:
sudo rm -f /etc/sudoers.d/90-cloud-init-users
-
Finally, disable
cloud-init
completely to prevent this from being overwritten:sudo touch /etc/cloud/cloud-init.disabled
Alternatively, disable
cloud-init
from managing anysudo
configuration for the user:sudo nano /var/lib/cloud/instance/user-data.txt
Look for the
NOPASSWD:ALL
option and comment or remove it:- sudo: ALL=(ALL) NOPASSWD:ALL + #sudo: ALL=(ALL) NOPASSWD:ALL
-
-
Clear the VM's Bash history:
history -c cat /dev/null > ~/.bash_history && history -c && history -w
[!NOTE]
There should be no other active sessions on the VM while doing this. -
Reboot the VM to apply all changes:
sudo reboot now
These configuration options are expected to be applied on top of the changes that were made to the base VM inherited by the extended VM:
-
Perform a full system upgrade on the system using
apt
, especially if it's been a while since the base VM was last upgraded. -
Set a static IP address and update the DNS server for the VM.
This details the process of updating certain networking configurations on the system.
This details the process of setting a static IP address and updating the DNS server on a system.
-
Ensure the system's network configuration is not provisioned by
cloud-init
:ls -l /etc/netplan/*-cloud-init.yaml
If the output is not empty, then the system is provisioned by
cloud-init
and needs to be disabled. -
To disable
cloud-init
from managing the system's network configuration:sudo nano /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
Add and save the following configuration to the file:
network: {config: disabled}
-
Backup the existing network configuration file, if available:
sudo cp /etc/netplan/00-installer-config.yaml /etc/netplan/00-installer-config.yaml.bak
-
Determine the name of the active network interface on the system:
route | grep '^default' | grep -o '[^ ]*$'
Sample output:
enp6s18
Alternatively, you may also use the following command:
ip link
Sample output:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: enp6s18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether fg:LA:Og:mQ:LQ:7Q brd ff:ff:ff:ff:ff:ff
In this example,
enp6s18
is the name of the active network interface. -
Update or create the network configuration file:
sudo nano /etc/netplan/00-installer-config.yaml
-
Update the configuration as such:
Original configuration which uses DHCP to dynamically assign an IP address:
network: ethernets: <network-interface>: dhcp4: true version: 2
Make the following changes:
network: ethernets: <network-interface>: + addresses: [<ip-address>/24] - dhcp4: true + dhcp4: false + nameservers: + addresses: [<dns1>,<dns2>] version: 2
The following is an example of the updated configuration:
network: ethernets: enp6s18: addresses: [192.168.0.106/24] dhcp4: false nameservers: addresses: [1.1.1.1,8.8.8.8] version: 2
-
Save all changes made to the file and apply the new configuration:
sudo netplan apply
This details the simple process of updating system's hostname:
-
Update the system's hostname using
hostnamectl
:sudo hostnamectl set-hostname "<hostname>.<domain>"
As an example, if the new
<hostname>
isubuntu-1
and the<domain>
isexample.com
:sudo hostnamectl set-hostname "ubuntu-1.example.com"
-
(Optional) If you intend to update the system's hosts file, Ensure the system's hosts file is not managed by
cloud-init
:cat /etc/hosts
If the start of the hosts file has the following disclaimer, then it is likely managed by
cloud-init
and needs to be disabled:# Your system has configured 'manage_etc_hosts' as True. # As a result, if you wish for changes to this file to persist # then you will need to either # a.) make changes to the master file in /etc/cloud/templates/hosts.debian.tmpl # b.) change or remove the value of 'manage_etc_hosts' in # /etc/cloud/cloud.cfg or cloud-config from user-data #
-
To disable
cloud-init
completely:sudo touch /etc/cloud/cloud-init.disabled
-
(Optional) Update the system's hosts file:
sudo nano /etc/hosts
Sample of the original hosts file:
127.0.0.1 localhost 127.0.1.1 ubuntu-server # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
As an example, update the hostname from
ubuntu-server
toubuntu-1
:127.0.0.1 localhost - 127.0.1.1 ubuntu-server + 127.0.1.1 ubuntu-1 # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
-
Reboot the VM to apply the changes:
sudo reboot now