Tom Butler's programming blog

v.je - A 225mb Minimal web development virtual machine

Introduction

v.je is a small and very easy to use virtual server. It's designed for anyone who wants a quick to download and set up web development server with a recent PHP version. It's been built with portability and ease of use in mind. The server will run pages on the url http://v.je/ after it's been booted with the vagrant up command and no further configuration.

Features

  1. 225mb box download size
  2. No configuration needed. Run vagrant up and your website is hosted on http://v.je/
  3. Without configuration host different websites on v.je subdomains e.g. http://subdomain1.v.je/ and http://subdomain2.v.je/
  4. No provisioning: Box boots up fast
  5. Includes up to date PHP development software*:
    • PHP
    • NGINX
    • MariaDB
    • Git
    • Composer
    • PHPUnit
  6. Portability: You can run vagrant halt on one machine, then vagrant up on another and your website will be visible. The database is automatically imported and exported. Most boxes store the database inside the VM. If you move your files to a different PC the database is lost. That's not the case with this box.
  7. URL rewriting is already set up. A request to any file that does not exist is sent to index.php

The box is updated with the latest versions every few months. It includes the most recent supported version of PHP and other software, at the time of writing PHP 7.2

Basic Usage

Make sure you have Vagrant and VirtualBox installed

  1. Using a terminal application (on windows, command prompt or powershell) navigate to the directory you want to store your files in
  2. Run vagrant init vje/min
  3. Run vagrant up
  4. Navigate to http://v.je/ in our web browser. If the server is running you will see a welcome page
  5. Write your PHP scripts to the websites/default/public directory which has been created.

MySQL

Connect to MySQL from inside or outside the VM with the details:

  • Host: v.je
  • Port: 3306
  • Username: v.je
  • Password: v.je

This MySQL user has full access to create schemas and other users. I suggest using MySQL Workbench from outside the VM to manage your database

Hosting multiple websites

The virtual machine is configured to host multiple websites from different folders within the created websites directory. To create a new website which is available on http://mysite.v.je/:

  1. Create the directory mysite inside the websites directory.
  2. Create the directory public inside the mysite directory.
  3. Place your web-accessible files inside the websites/mysite/public directory. For example, the file in websites/mysite/public/phpinfo.php will be accessible on the URL http://mysite.v.je/phpinfo.php

Any directory you create inside the websites directory is treated as a subdomain of v.je

Background

This was originally developed for my university students. Each week in class, students will run vagrant up to boot their server. It's not practical to store a provisioned VM on the network for each student and each student may use a different lab machine each week. Each time the student logs into a new machine, the box is downloaded, provisioned and created on that machine. When they use a new machine the following week, the process happens again.

Under normal circumstances a developer would use the same machine all the time, they'd only download and provision the box once. However, if 10 different students ran vagrant up on one of the lab machines, 10 copies of the virtual machine would be created. As there are over 120 students on the course, it's possible that over the course of the academic year, 50+ individuals might use the same physical lab machine at some point. If each student had a provisioned VM stored on each lab machine they happened to have used, it would take up a considerable amount of space.

We decided to clean up VMs on log out to avoid this issue. When a student logs out, any virtual machines they have created are destroyed. The next week they come to class the machine is downloaded, provisioned and booted once again.

I couldn't find an existing VM that met all of my needs, none of the existing solutions I looked at were ideal.

Puphpet is a common tool for provisioning web development focussed VMs, however the provisiong script takes what seems like forever. Normally this isn't an issue as it would only happen once and the provisioned VM would be booted each time. However, for my students this is an issue. I don't want students to have to sit there waiting for the box to be provisioned for 10 minutes before they can do any actual work. I timed the provisioning script (without counting the time it takes to download the 1gb+ base centos/ubuntu/whatever box) for a fairly minimal puphet script... it took 8 minutes.

I don't want my students to have to sit there waiting for a 1gb+ bare bones image to download, then have to wait for the best part of 10 minutes for all the packages to be downloaded and installed on top of that before they can do any work each week.

I wanted a box that was:

  1. Comparatively small to download
  2. Quick to start from a fresh installation
  3. Completely portable: You should be able to store the Vagrantfile and the application code on a network drive or memory stick and run vagrant up to restore the exact application state you left it when you halted the VM on another machine
  4. Didn't require any system wide conifguration on the host machine to host multiple websites (Unsurprisingly, students do not have write access to the HOSTS file on the lab machines!)
  5. Didn't require cloning a repository to download the Vagrantfile. It's an extra step that isn't really necessary. A simple vagrant init should suffice

As the goal is teaching programming, creating parity between production and develoment machines is less of an concern here. I needed a box that quickly and easily set up a development environment.

To achieve those goals I created a box with the following characteristics:

No provisioning

To remove the need for provisioning I build a box with all the relevant packages already installed and configured. I installed NGINX, PHP, MariaDB, Git, PHPUnit and Composer rather than having them downloaded and installed as part of the provisioning process. This significantly speeds up the time it takes for the vagrant up command to execute the first time.

Keeping the file size low

I used Arch Linux as a base as it's incredbily minimal and you can choose which packages are installed with a fair amount of control. Yes, it's not a typical web server distro but it works well for this case.

With NGINX, PHP, MariaDB, Git, Composer and PHPUnit installed (along with all their dependencies), the box is a 225mb download. On disk, it's just under 700mb extracted. For comparison, at the time of writing, the latest version of Laravel Homestead (v5.2.0) with the same virtualbox provider is a 1.5gb download, a full 1.25 gigabytes more! And that's before any provisioning has taken place!

Here's a few tricks I used to keep the footprint low:

  • The box does not include pacman or any of its numerous dependencies. I can install software by mounting the virtual disk in another archlinux virtual machine and using pacman --root.
  • Remove the 250mb linux-firmware package. Although this is a dependency of the linux kernel, pacman -Rdd linux-firmware can save 250mb with no adverse affects as the virtual machine doesn't need much hardware support
  • Use systemd-boot instead of grub. Grub and its dependencies are fairly large. Systemd is required anyway so it's already installed.
  • Remove man pages and unecessary locales
  • Remove kernel modules from /usr/lib/modules/*/kernel/drivers. The only modules required for virtualbox are net and acpi. Every other subdirectory can be removed.
  • Remove libgo.so* and libgfortran.so* from /usr/lib and save 60mb. Unless you're going to run GO or Fortran applications on the VM, these take up a lot of space
  • Remove /boot/initramfs-linux-fallback.img. You are removing the backup kernel, but it's a VM, it's easy enough to fix if something goes wrong.
  • Empty /usr/lib/modules/*/kernel/buid Arch keeps a copy of the built kernel and its modules here. You only need the kernel in /boot, delete the copy.
  • Delete /var/lib/mysql/ib_logfile0 and ib_logfile1. These log files take up 50mb each and aren't required.

After this, the packaged box as of 23/03/2018 is 225mb.

Portability

One thing I wanted to be able to do is get students to be able to run vagrant halt on one machine and vagrant up on another and host the same set of websites and restore the previous state.

For files, this is easy, all their scripts are stored in a shared folder on the host. However, for the database it's a bit more tricky. It is possible to store the database files on a shared folder, but I tried this an experienced issues with database corruption. Instead, the database is exported automatically when shutting down the machine and imported whenever the machine is booted.