VirtualBox: How to Configure an Ubuntu 20.04 Virtual Machine for Web Development

When developing for the web we need a local development environment. The simplest way to create a local virtual server for web development is probably to use Oracle VirtualBox. Then you could have as many web dev servers on different virtual machines as you need.

It’s very important to set up your virtual machines properly right from the start. Or you could get a lot of misconfiguration or performance problems. Which could make work a nightmare.

We’ll set up virtual machines with Ubuntu 20.04 on Oracle VirtualBox here. I normally use VirtualBox on Mac and Windows computers. So I am considering installation and configuration here both on computers running Windows 10 and macOS Big Sur.

And we’ll consider how to configure the virtual machines for the best performance.

I hope it will be useful to web developers, webmasters, or any enthusiasts who need to use VirtualBox for web development.

1. Install Oracle VirtualBox and an Ubuntu 20.04 Virtual Machine

The installation process is pretty straightforward. So, I’ll describe it very briefly.

If setting up a virtual machine is too elementary for you, please skip to section 2. Configure the Network. This is where some interesting stuff begins.

First, you need to install the Oracle VirtualBox.

On macOS Big Sur I’ve installed version 6.1.16. Still, on macOS Catalina and Windows 10, I installed version 6.1.2 which was available from the older builds on the VirtualBox official site. Because I had some performance problems with version 6.1.12 (version 6.1.12 was the latest version at the time of this article’s initial writing).

The installation of Oracle VirtualBox is pretty straightforward. You will have an exe-file for Windows on a dmg-file for macOS. Just run the installer and follow the instructions.

When the VirtualBox software has been installed, you’ll need to create a new virtual machine. It’s a little bit less evident. So let’s consider it.

I’ll show it for a Mac computer. But for Windows, it is the same.

To create a virtual machine, run the Oracle VirtualBox and click the button VirtualBox Button New New:

Create a Virtual Machine (macOS)
Create a Virtual Machine

In a window that opens, enter the virtual machine name, select the operating system, and click Continue:

Enter the Virtual Machine Name (macOS)
Enter the Virtual Machine Name

On the next screen, set the RAM for the virtual machine. The default is 1GB. I usually set it to 4GB. But it depends on how much memory you have on the host machine and how much you are ready to spare. In any case, you’ll be able to increase or decrease the virtual machine RAM after the virtual machine has been created.

Set RAM for Virtual Machine (macOS)
Set RAM for Virtual Machine

Then you’ll need need to choose the Create the virtual hard disk now option which is the default:

Virtual Hard Disk (macOS)
Virtual Hard Disk

Next, you’ll be asked to choose the virtual disk file type. I keep it at the default setting which is VDI:

Choose Hard Disk File Type (macOS)
Choose Hard Disk File Type

Next, I always choose the hard disk to be Dynamically allocated. It allows me to set the disk size to some bigger value e.g. 100GB. But initially, the disk will be small (so the virtual machine will not take a lot of space on the host machine’s hard disk right away). And additional space will be allocated when necessary.

Choose Dynamically Allocated Disk (macOS)
Choose Dynamically Allocated Disk

And finally, set the virtual hard disk file size. I usually set the size to some bigger value, e.g. 100GB, since I always use a dynamically allocated disk (which means it will be small at first and will grow when necessary). You never know how much additional space you’ll need. So I find it convenient to set the disk size big enough.

Set the Virtual Hard Disk File Size (macOS)
Set the Virtual Hard Disk File Size

Now we need to install the Ubuntu operating system. So, you need to download the Ubuntu 20.04 Server iso image. You can do it here (or just Google for “ubuntu server download”).

After we have downloaded the iso image, select your newly created virtual machine in the left panel and click the button VirtualBox Settings Button Settings:

Select Your VM and Click the Button Settings (macOS)
Select Your VM and Click the Button Settings

In the window that opens, select the tab Storage. And add the iso file to the virtual machine optical drive:

Add iso File to VM Optical Drive (macOS)
Add iso File to VM Optical Drive

Also, make sure that it is set for the virtual machine to boot from the CD-ROM first. It is done on the tab System of the same Settings window and this is the default boot sequence.

Now when you VirtualBox Button Start start your virtual machine, the Ubuntu 20.04 installation from the virtual CD-ROM will start.

The Ubuntu installation process is step by step and pretty straightforward. So I am not describing it here.

After Ubuntu has been installed, you need to install packages you’ll need for web development. The easiest way is probably to use Docker. In this way, you can easily experiment with e.g. different versions of PHP, MySQL, etc. by running different Docker containers for them. E.g. installing WordPress with XDebug in a Docker container is described in this article.

2. Configure the Network

There are 2 ways to configure the network for a Linux virtual machine:

  1. using the VirtualBox Bridged network adapter
  2. using 2 adapters at once: NAT adapter (for the VirtualBox to access the Internet) and Host-only adapter (for you to access the VM locally by SSH, HTTP, etc.)

Each of these 2 ways has its own cons and pros (described in the sections below).

You need to choose one of these 2 ways (not both of them). To proceed with the bridged network adapter configuration, continue to section 2.1. To proceed with NAT + Host-only adapters configuration, go to section 2.2.

2.1 Configure the Network on a Bridged Adapter

In this case, your VM will look and behave like a separate computer in your local network. It will have a separate from your host machine IP address. And it will be accessible in your local network (not just from your host machines but from other computers as well) by this IP address.

There’re Pros and Cons for this method:

Pros:

  1. Simpler to configure
    You need only 1 network adapter. And you do not need additional VirtualBox configurations (like having a NAT network or adding a Host-only adapter – like in section 2.2)
  2. VM is accessible from anywhere on your local network by the VM IP address.
    So your server could be easily shown e.g. to your co-workers.

Cons:

  1. Requires a router (yes, physical device). No router? See section 2.2.
  2. VM is accessible from anywhere on your local network
    So, anyone on your local network could try to access it (and maybe it’s not what you want).
  3. VM takes a separate (from your host machine) IP address on your local network
    So you could need to talk to your system administrator or warn your colleagues or they could try to acquire the same IP and you would get some IP conflicts.

2.1.1 Configure the Bridged Adapter for the Virtual Machine

Your VM with a bridged adapter will appear as a separate computer in your local network.

Let’s configure it.

Turn off your virtual machine if it is on.

MacOS: For your VM go to VirtualBox Settings Button Settings > Network and set the Adapter 1 to the Bridged Adapter:

Virtual Machine Bridged Adapter Configuration
Virtual Machine Bridged Adapter Configuration

The default Adapter Type would be Intel Pro/1000 Mt Desktop. You can leave it as is. I have set it to Paravirtualized Network (virtio-net) hoping for some performance improvement.

Windows: For your VM go to VirtualBox Settings Button Settings > Network and set the Adapter 1 to the Bridged Adapter:

Virtual Machine Bridged Adapter Configuration (Windows)
Virtual Machine Bridged Adapter Configuration

2.1.2 Set a Static IP for the Ubuntu Virtual Machine

Let’s set the static IP address for our Ubuntu 20.04 Virtual Machine.

In my case, I have configured (in my router settings) my local network to use the subnet 192.168.1.0/24 with my router having IP address 192.168.1.1. My host machine has the static IP 192.168.1.50. And I’ve decided to set the static IP address 192.168.1.98 for my Ubuntu 20.04 VM.

Log in to your Virtual Machine. In the Ubuntu 20.04 console run:

cd /etc/netplan/
sudo nano 00-installer-config.yaml

The initial file contents will be:

# This is the network config written by 'subiquity'
network:
  ethernets:
    enp0s3:
      dhcp4: true
  version: 2

For the static IP 192.168.1.98 change the file contents to:

network:
  ethernets:
    enp0s3:
      dhcp4: false
      addresses: [192.168.1.98/24]
      gateway4: 192.168.1.1
      nameservers:
         addresses: [8.8.8.8, 8.8.8.4]
  version: 2

I used Google DNS servers here: 8.8.8.8, 8.8.8.4. You can use any DNS servers you wish instead of course.

Now to apply the changes, run:

sudo netplan apply

2.1.3 Add the Static IP to the File hosts on the Host Machine

At this stage, we can access our virtual machine by its IP address 192.168.1.98 only. To be able to access our virtual machine from our host machine by some hostname (domain name), we need to add this name to the file hosts. This file is called the same (hosts) on Mac, under Windows, and under Linux.

MacOS: On Mac we can edit the hosts file like this:

sudo nano /etc/hosts

Add the following line to the file:

192.168.1.98    mytestsite.com

and save the file.

On the previous versions on MacOS I also had to flush Mac’s DNS cache like this:

sudo killall -HUP mDNSResponder

but on macOS Big Sur (and macOS Catalina) it does not seem to be required any longer.

Now if you e.g. run an HTTP server on your VM, you would be able to access it from the browser prompt by your domain name – in our case mytestsite.com.

Please note that on your host machine the domain name mytestsite.com will always resolve to 192.168.1.98 now. So if mytestsite.com really exists on the Internet, you will be accessing 192.168.1.98 instead.

Windows: If your host machine is under Windows, then (if Windows is installed by the default path) your hosts file could be found by the following path: c:\Windows\System32\drivers\etc\hosts

You will add the same (as for the macOS) line to the file:

192.168.1.98    mytestsite.com

And then you will need to run

ipconfig /flushdns

from the Windows terminal to flush the DNS cache under Windows.

Linux: Under Linux, the file hosts is normally located in the directory /etc. So you edit it like:

sudo nano /etc/hosts

and add the same line as in the examples above:

192.168.1.98    mytestsite.com

2.2 Configure the Network on NAT + Host-only Adapters

We are using 2 network Adapters in this case:

  1. NAT adapter – for our Virtual Machine to access the Internet
  2. Host-only adapter – for us to access the VM locally by SSH, HTTP, etc.

In this case, our VM will be available by its IP address from our host machine only. It will not be available from other computers on our local network. It does not require a router at all. It does not consume a separate IP on the local network.

There’re Pros and Cons for this method:

Pros:

  1. No router required
  2. VM is not accessible from other computers
    It does not take an additional IP address in your local network. It can not be accessed from other computers (if it’s what you want).

Cons:

  1. A little bit more difficult to configure
    You need to configure 2 network adapters.

2.2.1 Add a NAT Network

MacOS: Open the VirtualBox Preferences menu (keyboard shortcut: ⌘,):

VirtualBox Preferences Menu (macOS)
VirtualBox Preferences Menu

In the window that opens add the NAT network:

NAT Network Added (macOS)
NAT Network Added

In my case the NAT network has been added with the following settings:

NAT Network Settings Example (macOS)
NAT Network Settings Example

Please notice that this is a completely new network. E.g. my network (where the host machine is located) is 192.168.1.0/24. But this new NAT network is 10.0.2.0/24. It is a completely different network of course.

Windows: In the Oracle VirtualBox window click File > Preferences:

VirtualBox Preferences Menu (MS Windows)
VirtualBox Preferences Menu

In the window that opens add the NAT network and click the OK button:

NAT Network Added (MS Windows)
NAT Network Added

In my case the NAT network has had the following parameters:

NAT Network Settings Example (MS Windows)
NAT Network Settings Example

2.2.2 Add a Host Network

MacOS: With VirtualBox window in focus in the menu at the top of the screen click File > Host Network Manager...

Main Menu: File > Host Network Manager... (macOS)
Main Menu: File > Host Network Manager…

In the window that opens add a host network:

Host Network Added (macOS)
Host Network Added

In my case, the network created for me was 192.168.56.1/24. Please notice that it is quite different from the network where my host machine is located (my physical router network) which is 192.168.1.0/24.

Windows: Click File > Host Network Manager...

Main Menu: File > Host Network Manager... (Windows)
Main Menu: File > Host Network Manager…

Add the host network:

Host Network Added (Windows)
Host Network Added

2.2.3 Configure the NAT Adapter for the Virtual Machine

Turn off your virtual machine if it is on.

MacOS: With your VM selected go to VirtualBox Settings Button Settings > Network and set the Adapter 1 to the NAT Adapter:

Virtual Machine NAT Adapter Configuration (macOS)
Virtual Machine NAT Adapter Configuration

Windows: With your VM selected go to VirtualBox Settings Button Settings > Network and set the Adapter 1 to the NAT Adapter:

Virtual Machine NAT Adapter Configuration (Windows)
Virtual Machine NAT Adapter Configuration

2.2.4 Configure the Host-only Adapter for the Virtual Machine

Turn off your virtual machine if it is on.

MacOS: With your VM selected go to VirtualBox Settings Button Settings > Network and set the Adapter 2 to the Host-only Adapter:

Virtual Machine Host-only Adapter Configuration (macOS)
Virtual Machine Host-only Adapter Configuration

Choose the network name from the Name drop-down.

Windows: With your VM selected go to VirtualBox Settings Button Settings > Network and set the Adapter 2 to the Host-only Adapter:

Virtual Machine Host-only Adapter Configuration (Windows)
Virtual Machine Host-only Adapter Configuration

2.2.5 Set a Static IP for the Ubuntu Virtual Machine

Since the host network is 192.168.56.1/24 (see section 2.2.2), we must set the static IP of our virtual machine in this network. Let’s configure it as 192.168.56.2.

Log in to your Virtual Machine and run in the Ubuntu 20.04 console:

cd /etc/netplan/
sudo nano 00-installer-config.yaml

For the static IP 192.168.56.2 change the file contents to this:

network:
  ethernets:
    enp0s3:
      dhcp4: true
    enp0s8:
      dhcp4: false
      addresses: [192.168.56.2/24]
  version: 2

Here we’ve configured the network interface enp0s3 for the NAT network (the IP of the VM in this network is obtained via DHCP) and the network interface enp0s8 for the host-only network (where we’ve configured the static IP address 192.168.56.2 for the VM).

2.2.6 Add the Static IP to the File hosts on the Host Machine

In our case, the VM will be accessible from on host machine by the IP address 192.168.56.2. The VM can be accessed by this IP from the host machine only (where we run the VirtualBox). We can’t access the VM from any other computer on our local network.

To be able to access the VM not only by the IP address but also by a hostname (domain name) from our host machine, we need to add the hostname to the file hosts on the host machine:

192.168.1.98    mytestsite.com

How to add this line to the file hosts under macOS, Windows and Linux is described in section 2.1.3 above.

Additional reading for section 2.2: you could also check this article on how to configure a web-development environment for a Debian VM using NAT + Host-only adapters.

3. Add Guest Additions to the Ubuntu Virtual Machine

3.1 Install Required Packages on the Ubuntu Virtual Machine

Login to your Ubuntu 20.04 virtual machine terminal. First you need to update all Ubuntu packages:

sudo apt update & apt upgrade -y

Now run:

sudo apt install linux-headers-$(uname -r) dkms build-essential

Technically dkms is not required for the Guest Additions to work. But if you do not install the package, the Guest Additions would stop working after the next VM kernel update. So it is better to install this package right away (it must be installed before installing guest additions).

Now you need the file VBoxGuestAdditions.iso of the same version as the version of your VirtualBox.

For macOS users: under macOS Catalina, I had to use VirtualBox 6.1.2. Because I had some performance problems with 6.1.12 (the most recent version of VirtualBox at that time). But when I upgraded to macOS Big Sur, version 6.1.2 did not work for me anymore. So under macOS Big Sur, I am using the most recent version of VirtualBox (which is 6.1.16 at the time of writing this text). But if you are still under macOS Catalina and having any performance problems with your virtual machines, you could try downgrading the VirtualBox to version 6.1.2.

You can locate the file VBoxGuestAdditions.iso on the computer where your VirtualBox has been installed:

3.2 Locate VBoxGuestAdditions.iso on your computer

MacOS: Go to your Applications directory, locate the file VirtualBox, right-click on it, select Show Package Contents:

Locate VBoxGuestAdditions.iso on Mac: Step 1
Locate VBoxGuestAdditions.iso on Mac: Step 1

This will open the package contents. There under Contents > MacOS locate the file VBoxGuestAdditions.iso:

Locate VBoxGuestAdditions.iso on Mac: Step 2
Locate VBoxGuestAdditions.iso on Mac: Step 2

Copy the file VBoxGuestAdditions.iso to any directory on your computer.

Windows: The file VBoxGuestAdditions.iso could be found in the directory where the VirtualBox is installed. By default it is: c:\Program Files\Oracle\VirtualBox\

Just right-click on the VirtualBox desktop shortcut and see the installation path for the VirtualBox on your computer:

VirtualBox Desktop Shortcut Right-Clicked
VirtualBox Desktop Shortcut Right-Clicked

The file VBoxGuestAdditions.iso will be in the same folder as VirtualBox.exe.

3.3 Install Guest Additions on the Ubuntu Virtual Machine

Turn off your VM if it is on.

macOS: In the VirtualBox interface for your VM go to VirtualBox Settings Button Settings > Storage. Add the disk file VBoxGuestAdditions.iso to your VM Optical Drive:

VBoxGuestAdditions.iso in the VM Optical Drive
VBoxGuestAdditions.iso in the VM Optical Drive (macOS)

Windows: The same as for Mac, for your VM go to VirtualBox Settings Button Settings > Storage. Add the disk file VBoxGuestAdditions.iso to your VM Optical Drive:

VBoxGuestAdditions.iso in the VM Optical Drive (Windows)
VBoxGuestAdditions.iso in the VM Optical Drive (Windows)

You do not need to boot from this optical drive.

Turn your VM on.

Now mount this optical drive to some directory on the Ubuntu VM. In the Ubuntu terminal run (I am mounting to the subdirectory cdrom of the current Ubuntu user home directory – you can mount to any other directory of course):

cd
mkdir cdrom
sudo mount /dev/cdrom cdrom

You’ll get the message “WARNING: device write-protected, mounted read-only.”. It’s OK of course – a CD-ROM is supposed to be read-only.

Now run:

cd cdrom
sudo ./VBoxLinuxAdditions.run

In the end, you’ll get the message “VirtualBox Guest Additions: Running kernel modules will not be replaced until the system is restarted”. So reboot the VM for the changes to take effect:

reboot

Please also check the 4.1 Introduction to Guest Additions section of the VirtualBox documentation.

3.4 Check that the Guest Additions have been installed successfully

To check the Guest Additions have been really installed, run this command from the VM console:

lsmod | grep vbo

If the Guest Additions have been installed successfully, you will see vboxguest in the results. E.g. in my case the result looked like this:

Check that VM Guest Additions were installed
Check that VM Guest Additions were installed

Also you could check this reply and this Youtube video.

4. How to Share a Folder Between the Host Machine and a Virtual Machine

Our VM runs a Linux server (Ubuntu 20.04 Server in our case). You could use some console editor like vim or nano for web development there of course, but I doubt it would be convenient for any serious programming work. Normally you need an IDE for web development (e.g. PhpStorm or Netbeans). So normally you install your IDE on your host machine and somehow give it access to the folder on the VM (which is a guest machine) where your project source files are located.

There are 2 main ways to make the folder with project source files available to the IDE on the host machine:

  1. Create a folder on the host machine and make it available to the VM (see section 4.1).
    The project files are located in a folder on the host machine. Your IDE will work fast with them. But if there are a lot of file operations on your web server (which is normal for many frameworks and CMS), this will slow your system down.
  2. Create a folder on the VM and make it available to the host machine (see section 4.2).
    The project folder is located on the VM and shared with the host machine. File operations on the VM will run fast. But the IDE on the host machine will have slow access to the files.

Some IDEs (e.g. PhpStorm) could have serious performance problems if the web project is installed on a network share. So it is much better to use the 1st scenario (see section 4.1) with such IDEs.

In the 2nd scenario, you could be forced e.g. to turn off some sync features and built-in Git support in PhpStorm. Just to make the IDE work faster.

Let’s consider both approaches.

4.1 How to Share a Folder on the Host Machine with an Ubuntu 20.04 Virtual Machine Using Guest Additions

In this scenario, you have a folder on your macOS host machine’s hard drive. And you want to share this folder for the VirtualBox virtual machine.

If you use this for your project folder, your IDE (which is installed on your host machine) with work fast with your project files. But any file operations on the VM will work slower.

There are 2 ways to mount a host machine folder on a VM using Guest Additions:

  • Auto-mounting – see section 4.1.1.
    Auto-mounting could be done quickly. But it is less flexible than manual mounting. And if used with web project folders, requires additional configuration changes. Auto-mounting could be recommended for some simple tasks only – when you need to mount some folders quickly. But for web project folders, manual mounting is probably more suitable.
  • Manual mounting – see section 4.1.2.
    Manual mounting requires running a few more commands in the terminal. But this type of mounting is much more flexible. And does not have many problems auto-mounting has. It is more suitable for web projects.

4.1.1 Auto-mounting

4.1.1.1 Auto-mount a Shared a Folder Using Guest Additions

1. Turn on your VM.

2. On the VM under Ubuntu install the packages virtualbox-guest-dkms and virtualbox-guest-utils:

sudo apt-get update
sudo apt-get install virtualbox-guest-dkms
sudo apt-get install virtualbox-guest-utils

(source)

3. Turn off your VM.

4. Go to the VM VirtualBox Settings Button Settings. In the dialog that opens click the item Shared Folders. Then click the button Add new shared folder.

Add a New Shared Folder
Add a New Shared Folder

5. In the dialog that opens select the path to an existing folder on your host machine. Check the option Auto-mount if you wish the folder to get auto-mounted. Click OK.

Add Shared Folder Parameters for Auto-mount
Add Shared Folder Parameters for Auto-mount

6. Start your VM.

By default, a folder on the host machine will be mapped to a subfolder /media on the Ubuntu guest machine. The mapped subfolder name will be the same as the name of the folder on the host machine but prefixed with sf_ (where sf obviously stands for shared folder).

E.g. in my case the folder wpdiaries/ on the host machine will be mapped to /media/sf_wpdiaries/ on my VM running Ubuntu 20.04.

4.1.1.2 Redefine Guest Properties

By default, your host machine directory will be mounted as a subdirectory of the directory /media on the virtual machine. It is not what is always wanted. E.e. if we use Apache as the HTTP server, we would probably want to mount our web project directory as a subdirectory of /var/www/ rather than of /media/.

Also, all mounted directories have the prefix sf_ by default. Probably it would be good to remove this prefix.

According to the Guest Additions documentation, to change the directory on the guest machine where the host machine directories are mounted, and to change the mounted directory name prefix from sf_ to something else, we need to redefine the guest properties /VirtualBox/GuestAdd/SharedFolders/MountDir and /VirtualBox/GuestAdd/SharedFolders/MountPrefix correspondingly.

7. Let’s redefine the guest property /VirtualBox/GuestAdd/SharedFolders/MountDir.

Before you share a directory from the host machine to the guest machine, some sources recommend to create a mounted folder on the VM in advance and give it full permissions. I did not do this. The folder on the guest machine gets created automatically when you mount it from the host machine. I did not even change permissions of the parent folder /var/www/ (which are 755 by default).

To redefine a guest property you need to use VBoxManage guestproperty commands. E.g. one of my virtual machines has a strange name WP_20_04 98 (I like to add the last byte of a VM IP address to the name of the VM – in this case, 98 stands for the IP 192.168.1.98).

To check which guest properties are defined for your VM currently: Turn on the VM (or no properties will be output). In the Mac terminal run the command:

VBoxManage guestproperty enumerate "WP_20_04 98"

but replace WP_20_04 98 with the name of your currently running VM. For me the output was the following:

List of All Guest Properties
List of All Guest Properties

Or we could check only properties starting with /VirtualBox/GuestAdd/:

VBoxManage guestproperty enumerate "WP_20_04 98" --patterns "/VirtualBox/GuestAdd/*"
List of Guest Properties Containing /VirtualBox/GuestAdd/
List of Guest Properties Containing /VirtualBox/GuestAdd/

As you can see, neither /VirtualBox/GuestAdd/SharedFolders/MountDir nor /VirtualBox/GuestAdd/SharedFolders/MountPrefix are defined for the VM yet. Let’s set /VirtualBox/GuestAdd/SharedFolders/MountDir:

VBoxManage guestproperty set "WP_20_04 98" "/VirtualBox/GuestAdd/SharedFolders/MountDir" "/var/www/"

This set command does not require the VM to be on or off. I tried both. And in both cases, the property was set for me. Though if you have the VM off, its settings open, and trying to update a guest property from a terminal, you’ll be asked if you wish to reload the changes made in the terminal or keep the old settings:

Settings Were Changed Externally Warning
Settings Were Changed Externally Warning

After running the set command, when your VM is on again, to make sure the guest property has been changed, run:

VBoxManage guestproperty enumerate "WP_20_04 98" --patterns "/VirtualBox/GuestAdd/*"

This time it should output something like:

Updated List of Guest Properties Containing /VirtualBox/GuestAdd/
Updated List of Guest Properties Containing /VirtualBox/GuestAdd/

After the VM is rebooted:

reboot

the shared folders will be mounted under /var/www/ on the VM.

E.g. in my case, the folder wpdiaries/ on the host machine will be mounted to /var/www/sf_wpdiaries/ on the VM (not to /media/sf_wpdiaries/ as before).

8. Probably it would be good to replace the default mounted directory prefix from sf_ to an empty string.

If you try one of these 2 commands:

VBoxManage guestproperty set "WP_20_04 98" /VirtualBox/GuestAdd/SharedFolders/MountPrefix
VBoxManage guestproperty set "WP_20_04 98" /VirtualBox/GuestAdd/SharedFolders/MountPrefix ""

it should not work. Because according to the official documentation, setting a property to an empty value should clear the property and reset it to the default value (which is "sf_" for the prefix).

Some sources (e.g. this) recommend setting the prefix to "/" to make the prefix an empty string:

VBoxManage guestproperty set "WP_20_04 98" /VirtualBox/GuestAdd/SharedFolders/MountPrefix "/"

Unfortunately under macOS Big Sur 11.1 with VirtualBox 1.1.16 and VM running Ubuntu 20.04 this did not work for me.

Sometimes I was able to set the directory prefix to an empty string. But after several reboots, it reverted back to the default sf_.

Currently, I left the default prefix as sf_ for all my projects. So e.g. a directory wpdiaries on Mac mounts to /var/www/sf_wpdiaries on the VM. I left it as is. Maybe not too pretty. But at least reliable.

Please notice: After you share your folder e.g. from your Mac to Ubuntu 20.04 VM using Guest Additions, your shared files and folders on the VM will be under user root and group vboxsf. For both with full permissions. But any permissions for “others” will be absent. So no program on the VM which is not under user root or in the group vboxsf will even be able to read these files. Including your HTTP server.

The user www-data (default Apache2 user under Ubuntu) will be unable to read files inside you web project directory. You’ll get a message like: You don’t have permission to access this resource. Server unable to read htaccess file, denying access to be safe.

Apache Permissions Problem (403 Forbidden)
Apache Permissions Problem (403 Forbidden)

The solution could be to add the user www-data to the group vboxsf on the virtual machine (on the local development server only, not accessible from the Internet of course, since giving Apache additional permissions is not really safe):

sudo usermod -a -G vboxsf www-data

An even more interesting situation could be if you e.g. run your WordPress project on your VM inside a Docker container:

4.1.1.3 Solve Docker Permissions Problem

In this section, I will assume that we are running a WordPress project inside a Docker container as described in the article on adding XDebug to a Docker WordPress container.

In this case, you can enter the Docker container console by running the following command in your VM console (here like in the article on XDebug I assume that the WordPress container name is wordpress-wpd):

docker exec -it wordpress-wpd bash

You will enter the Docker container terminal and find yourself inside the folder /var/www/html.

There you can check under which user and group your project files and folders have been mapped:

ls -la

In my case, my project files belonged to the user root and to group 998 which was not added to the container.

To check which groups exist in your container, you can run:

compgen -g | less

(source)

Also, you can check under which operating system your container is running:

cat /etc/*-release

(source)

In my case it was Debian.

And still being in the Docker container terminal, we can find out under which user the Apache is running:

ps aux | egrep '(apache|httpd)'

(source)

So we see that our project files belong to the user root, belong to a group with ID 998, which clearly corresponds to the group vboxsf inside the VM but is not defined inside the container. And Apache is run under the user www-data.

So inside our Dockerfile, we need to add a user group for the group ID we found out and add the user under which Apache is running to this group. To do that you can add the following 2 lines to the file Dockerfile:

RUN groupadd -g 998 vboxsf
RUN usermod -a -G vboxsf www-data

and then restart the container.

Just for the reference: the Dockerfile from section 1 of the article on XDebug with Docker will look like this now:

FROM wordpress:5.8.0-php8.0-apache

# Install packages under Debian
RUN apt-get update && \
    apt-get -y install git

# Install XDebug from source as described here:
# https://xdebug.org/docs/install
# Available branches of XDebug could be seen here:
# https://github.com/xdebug/xdebug/branches
RUN cd /tmp && \
    git clone git://github.com/xdebug/xdebug.git && \
    cd xdebug && \
    git checkout xdebug_3_0 && \
    phpize && \
    ./configure --enable-xdebug && \
    make && \
    make install && \
    rm -rf /tmp/xdebug

# Copy xdebug.ini to /usr/local/etc/php/conf.d/
COPY files-to-copy/ /

# Since this Dockerfile extends the official Docker image `wordpress`,
# and since `wordpress`, in turn, extends the official Docker image `php`,
# the helper script docker-php-ext-enable (defined for image `php`)
# works here, and we can use it to enable xdebug:
RUN docker-php-ext-enable xdebug

# This block is necessary only if the project folder is shared with Guest Additions:
# 1. Add the group vboxsf.
# 2. Add the user www-data to the group vboxsf.
# It will give Apache full permissions in the mounted directory.
RUN groupadd -g 998 vboxsf
RUN usermod -a -G vboxsf www-data

Of course this should be applied in the local development environment only. Anything like this would be very insecure for a server accessible from the Internet.

4.1.2 Manual Mounting

4.1.2.1 Manual Mounting a Shared a Folder Using Guest Additions

Manual mounting is probably the best way to mount a shared folder. We can mount under any user or group. To any folder. And without any prefix.

Suppose your web project files are in the folder myproj on your Mac host machine. And you want to mount this folder as /var/www/wpdiaries on your Ubuntu 20.04 VM.

The first steps are the same as steps 1 to 4 of section 4.1.1. Do them.

In step 5, in the dialog that opens choose the path to your project files folder in the field Folder Path. Enter the name under which you want the guest machine to see the folder in the field Folder Name. And make sure that the option Auto-mount is not checked.

Add Shared Folder Parameters for Manual Mount (macOS)
Add Shared Folder Parameters for Manual Mount (macOS)

In this dialog:

  • Folder Path is the path to the shared folder on the host machine.
  • Folder Name is the name of your share for the guest machine. This is the name under which the guest machine will see your shared folder. And this is the name you will use in the mount command on the guest machine.
  • Auto-mount must be unchecked this time. We will mount manually.

6. Let’s mount our project under the user www-data and group www-data (the user and group under which an HTTP server normally runs on Ubuntu).

Find the UID of the user www-data and the GID of the group www-data (we will need them in the mount command). In the Ubuntu terminal run:

id -u www-data
getent group www-data

(source)

In my case both the UID of the user www-data and the GID of the group www-data were 33.

Now you can mount your share (in my case wpdiaries) under UID=33 and GID=33 to the folder /var/www/wpdiaries on your VM:

mount -t vboxsf -o rw,uid=33,gid=33 wpdiaries /var/www/wpdiaries

where

  • -t vboxsf means that the filesystem type is vboxsf
  • -o means options. The following options are set:
    • rw means we mount the directory with read and write permissions
    • uid=33 means that the files and folders will be mounted under user ID 33 (under which an HTTP server normally runs on Ubuntu)
    • gid=33 means that the files and folders will be mounted under group ID 33 (under which an HTTP server normally runs on Ubuntu)
  • wpdiaries is the share name (which we have defined in the shared folder parameters in the dialog above)
  • /var/www/wpdiaries is the mount point i.e. the directory on the VM where our host machine folder will be mounted

If necessary, you could always unmount the share on your VM like this:

umount /var/www/wpdiaries

You should not be inside this directory in the terminal when unmounting. Or you’ll get the error message target is busy.

7. To automate mounting this shared folder each time you start your VM, you need to add the share to the file /etc/fstab under Ubuntu.

First, make a copy of the file fstab:

cp -prv /etc/fstab /etc/fstab.old

Add the following line to the file /etc/fstab. It will mount our share (in my case wpdiaries) to the folder /var/www/wpdiaries under UID=33 and GID=33:

wpdiaries  /var/www/wpdiaries vboxsf  auto,rw,uid=33,gid=33 0 0

(source)

8. Reboot your VM:

reboot

Also, since we added the option auto to our options in the file fstab, the command for mounting all shares should also work:

mount -a

Additional reading:
Official VirtualBox documentation on manual mounting
Ubuntu mount command reference
Ubuntu documentation on the fstab file format

4.1.2.2 Manual Mounting when Projects Are in Docker Containers

If you have your projects running in Docker containers as described in the article How to Add XDebug to the Official Docker WordPress Image, the approach could be similar.

Suppose you have a folder projects on your host machine. And in the subfolders of this folder, all your web projects are located. And you need to mount this folder projects from your (e.g. macOS) host machine to the folder /opt/projects/ on your VM (running Ubuntu 20.04). I.e. we work with our projects on the VM exactly as it was described in the article on using Xdebug with Docker.

The first steps are the same as steps 1 to 4 of section 4.1.1.

In step 5, in the dialog that opens, choose the path to the folder projects on your host machine in the field Folder Path. Enter the name under which you want the guest machine to see the folder in the field Folder Name (in our case I’ve entered projects). And make sure that the option Auto-mount is not checked.

Add Parameters for Sharing a Folder Containing Docker Projects (macOS)
Add Parameters for Sharing a Folder Containing Docker Projects (macOS)

6. Then, similar to how it has been done in step 6 of section 4.1.2.1, you could mount your directory projects as

mount -t vboxsf -o rw,uid=33,gid=33 projects /opt/projects

where

  • -t vboxsf means that the filesystem type is vboxsf
  • -o means options. The following options are set:
    • rw means we mount the directory with read and write permissions
    • uid=33 means that the files and folders will be mounted under user ID 33 (under which an HTTP server normally runs on Ubuntu)
    • gid=33 means that the files and folders will be mounted under group ID 33 (under which an HTTP server normally runs on Ubuntu)
  • projects is the share name (which we have defined in the shared folder parameters in the dialog above)
  • /opt/projects is the mount point i.e. the directory on the VM where our host machine folder will be mounted

And later you could unmount this folder as

umount /opt/projects

7. Now we need to deal with a MySQL named volume (if we have one) permissions problem:

Suppose we have a project configuration based on multiple docker-compose files in our development environment. Exactly as it was described in section 4.1 of the article How to Add XDebug to the Official Docker WordPress Image.

In that section, we had MySQL in a separate Docker container. The container name was mysql-wpd. And we had a named volume for the MySQL database:

    volumes:
      - /opt/projects/wpd/mysql:/var/lib/mysql

But the folder /opt/projects/ is a mounted (from the host machine) folder. And it is mounted under the user and group www-data.

So MySQL will be unable to write inside this folder. And the MySQL Docker container will be restarting endlessly (if we try to start it).

To avoid any permission problems, I prefer to move this database-related named volume out of the mounted folder. And place it in some (native Ubuntu) folder on the VM outside of the mounted folder /opt/projects. Let’s create a (normal Ubuntu) folder /opt/projects_local on the VM for storing our named volumes:

mkdir /opt/projects_local

To make that change in the development environment only, I add this volume to the file docker-compose.dev.yml (see section 4.1 of the article How to Add XDebug to the Official Docker WordPress Image). The following 2 lines need to be added to the very bottom of the file (in the section db):

    volumes:
      - /opt/projects_local/wpd/mysql:/var/lib/mysql

And the file docker-compose.dev.yml from section 4.1 of the article on adding XDebug to the official Docker WordPress image will take the form:

version: '3.8'

services:

  wordpress:
    build:
      context: ./xdebug # a path to a directory containing a Dockerfile, or a url to a git repository

    ports:
      - "80:80"

    environment:
      VIRTUAL_HOST: mydomain.com, www.mydomain.com
      # Set the XDEBUG_CONFIG as described here: https://xdebug.org/docs/remote
      XDEBUG_CONFIG: remote_host=192.168.1.2

  db:
    ports:
      -  "3306:3306"

    volumes:
      - /opt/projects_local/wpd/mysql:/var/lib/mysql

Now e.g. we can rebuild the WordPress container and start our containers with the command:

docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build

For more details on using multiple docker-compose files to start Docker containers with different configurations in the production and development environments, please see the article How to Add XDebug to the Official Docker WordPress Image. It is described there in detail.

8. Automating mounting out folder projects is done very similar to how it was done in section 4.1.2.1:

First make a copy of the file fstab:

cp -prv /etc/fstab /etc/fstab.old

And now add the following line to /etc/fstab:

projects /opt/projects vboxsf auto,rw,uid=33,gid=33 0 0

Reboot your VM after making this change.

Please notice: Before you shut down your VM, it’s always better to stop your Docker containers first. Otherwise, you could get a situation when the database gets corrupt and the MySQL Docker container begins to restart endlessly (when you try to start it).

4.2 How to Share a Folder on an Ubuntu 20.04 Virtual Machine using Samba Server

In the previous section, we have created a folder on the host machine and shared it to the guest machine. Here we’ll do the opposite. We’ll take a folder on an Ubuntu 20.04 virtual machine (guest machine) and share this folder. We will use the Samba server for it.

Of course everything described in this section should be used in local development environment only. If you use Samba for anything other than local development, you would need more secure settings.

1. Install the Samba server. In the console of your Ubuntu 20.04 VM run:

sudo apt-get install samba

Check where samba has been installed:

whereis samba

2. Find out your network workgroup:

  • Windows 10: System > About, click System info
  • Windows 7: Control Panel > System and Security > System
  • macOS: Preferences > Network, select your connection on the left (e.g. Wi-Fi), click the button Advanced on the bottom right, in the dialog that opens go to the tab WINS (which stands for Windows Internet Name Service) (source)

3. Edit /etc/samba/smb.conf. Under section [global] change the line (if your workgroup is not WORKGROUP, use your workgroup name instead):

workgroup = WORKGROUP

Under the same section [global] add the line

security = user

Also, if your host machine is a Mac and you do not want macOS to create service files starting with ._ or files like .DS_Store, you can add the following 2 lines under the same section [global]:

veto files = /._*/.DS_Store/
delete veto files = yes

(source)

4. Add the following block at the very bottom of /etc/samba/smb.conf:

[share98]
    comment = Ubuntu File Server Share
    path = /var/www
    browsable = yes
    guest ok = no
    read only = no
    create mask = 0755
    force user = root

Please notice: 1) I’ve used share98 as the name of the share (in the line [share98]). You could choose a different name of course. Personally, I like to name my shares after IPs on which my VMs are running (this VM is on IP 192.168.1.98). So that I could see at once which share is which.
2) The shared path on the VM is /var/www
3) guest ok = no means that only authenticated users can connect.
You can change this to yes while configuring the share. And in the case of yes, anyone would be able to connect (which is very insecure of course).
4) force user = root means that everyone who connects will have the root access. This is extremely unsafe of course. So I use this setting only in my local development environment – nowhere else. In general, you could use:

    force user = <user>
    force group = <group>

instead (substitute the required user and group in place of <user> and <group>).

5. Set your SMB user password:

smbpasswd -a root

Or if your user is not root (which is more secure of course):

smbpasswd -a mysmbuser

6. Restart the corresponding Ubuntu services:

service smbd restart
service nmbd restart

7. Now the shared folder will be available on your network. If you are on Mac you could mount it like this (the local directory where you mount the share must already exist and preferably be empty):

mount_smbfs //mysmbuser@192.168.1.98/share98 /path/to/a/local/directory/

Though in this case, you will be asked for a password for your SMB user mysmbuser.

Or you could use set the password right on the command line in the terminal (which is insecure of course):

mount_smbfs //mysmbuser:mysmbpassword@192.168.1.98/share98 /path/to/a/local/directory/

Later you could could unmount the directory like this:

umount /path/to/a/local/directory/

Normally I create 2 files with short names in my user home directory (e.g. ~/mnt and ~/umnt). The first contains the command for mounting the network share. And the 2nd contains the command for unmounting it. Running those files instead of typing the mount/umount commands each time saves time.

Additional reading:
Ubuntu documentation for Samba file server

5. Additional Performance Improvements

What we’ve done so far should make your VM work without performance problems already. At least this how it worked for me.

But if you wish to improve the virtual machine disk performance a little bit, you could try the approach described in this article.

What is proposed in that article is to go to VirtualBox Settings Button Settings > Storage, select the Controller: SATA and check the checkbox Host I/O Cache:

macOS:

Host I/O Cache
Host I/O Cache (macOS)

Windows:

Host I/O Cache
Host I/O Cache (Windows)

6. Deploy Your Website Locally

Probably the simplest way to deploy your web site for development locally is to use Docker. Please see this article on how to do that for a WordPress site. The article describes how to set a development environment with Docker for a WordPress site. And how to add XDebug to the Docker image (which would certainly be useful for development).

Or you could set LAMP and deploy a web site locally in the traditional way. This process is pretty straightforward and out of the scope of this article.

Conclusion

Why have I written all this in the first place? I’ve been using Oracle VirtualBox for web development for many years. But recently I’ve started getting performance problems with my virtual machines. And the more recent versions of the VirtualBox I used, the worse everything worked. I’ve ended up with some of my web pages sometimes loading instantly but sometimes after 10 seconds delay. The work has become intolerable.

The worst performance problems were on virtual machines where I used Docker containers.

At that time I was on VirtualBox 6.1.12, the host machine was running macOS Catalina, my virtual machines were on Ubuntu 20.04 and the VirtualBox Guest Additions were not installed. So I had to downgrade from VirtualBox 6.1.12 to 6.1.2, use 2 network adapters (NAT + Host-only), and install VirtualBox Guest Additions. This solved the performance problems for me.

Currently, on Mac, I’ve upgraded the operating system to Big Sur 11.1. VirtualBox 6.1.2 stopped working for me. So I am using the most recent version of VirtualBox (6.1.16 at the time of writing) currently. Also, I keep using 2 network adapters (NAT + Host-only). And VirtualBox Guest Additions are installed of course.

In my experience, it’s very important to configure your virtual machines properly right from the start…

I hope you’ve enjoyed the article.

If you have any questions or comments, I would be very glad if you posted them below.

All my articles are always work in progress. So, if you have anything to say, please do not hesitate to do it. Your comments will be highly appreciated.

Thanks for reading to the end!

6 thoughts on “VirtualBox: How to Configure an Ubuntu 20.04 Virtual Machine for Web Development”

  1. This is a FANTASTIC write-up! Thank you for putting this together -really. This is the stuff that I have to manually run through and figure out on my own every time I want to setup a new VM (my last VM is from 2007). As such, I’ve been putting it off.

    Thank you again!

    1. Thank you very much for the good words! I really appreciate them. I am very glad to hear you’ve found the article helpful.

      Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *