Rocky Linux
Rocky Linux is an open-source enterprise operating system designed to be 100% compatible with Red Hat Enterprise Linux® (RHEL).
Rocky Linux started when Red Hat announced that they would discontinue development of CentOS in December 2020.
Rocky Linux 9This Vagrant box contains stable versions:
Rocky Linux | Apache | MariaDB |
---|---|---|
9.1 | 2.4 | 10.6 |
PHP | Ruby | Python |
8.2 | 3.1.3 | 3.9 |
This Vagrant box contains stable versions:
Rocky Linux | Apache | MariaDB |
---|---|---|
8.7 | 2.4 | 10.6 |
PHP | Ruby | Python |
8.2 | 3.1.3 | 3.9 |
CentOS
CentOS is an open-source enterprise operating system compatible with Red Hat Enterprise Linux® (RHEL).
CentOS is discontinued; it was available from May 2004 to November 2021.
CentOS 8This Vagrant box contains stable versions:
CentOS | Apache | MariaDB | PHP | Python |
---|---|---|---|---|
8.5 | 2.4 | 10.6 | 7.4 | 2.7 3.6 |
This Vagrant box contains stable versions:
CentOS | Apache | MariaDB | PHP | Python |
---|---|---|---|---|
7.9 | 2.4 | 10.6 | 7.4 | 2.7 3.6 |
This Vagrant box contains stable versions:
CentOS | Apache | MySQL | PHP | Python |
---|---|---|---|---|
6.10 | 2.2 | 5.1.73 | 5.3.3 | 2.6 |
Debian
Debian is a GNU/Linux distribution composed of free and open-source software.
Debian 12This Vagrant box contains stable versions:
Debian | Apache | MariaDB |
---|---|---|
12 | 2.4 | 10.11 |
PHP | Python | Node.js |
8.2 | 3.11 | 18.13 |
This Vagrant box contains stable versions:
Debian | Apache | MariaDB | PHP | Python |
---|---|---|---|---|
11 | 2.4 | 10.6 | 8.2 | 3.9 |
This Vagrant box contains stable versions:
Debian | Apache | MariaDB | PHP | Python |
---|---|---|---|---|
10 | 2.4 | 10.6 | 8.1 | 2.7 3.7 |
Ubuntu
Ubuntu is a Linux distribution based on Debian and composed mostly of free and open-source software.
Ubuntu 22.04This Vagrant box contains stable versions:
Ubuntu | Apache | MariaDB | PHP | Python |
---|---|---|---|---|
22.04 | 2.4 | 10.6 | 8.2 | 3.10 |
This Vagrant box contains stable versions:
Ubuntu | Apache | MariaDB | PHP | Python |
---|---|---|---|---|
20.04 | 2.4 | 10.6 | 7.4 | 3.8 |
This Vagrant box contains stable versions:
Ubuntu | Apache | MariaDB | PHP | Python |
---|---|---|---|---|
18.04 | 2.4 | 10.6 | 7.4 | 2.7 3.6 |
Provisioning in Bash
When you add or remove Linux OS packages to your desktop or laptop, you must use Bash commands to execute their download and installation.
If you have to add or remove packages on a Linux server, you must use Bash commands too.
So the provisioning script provision.sh
is written in Bash for familiarity.
No need to learn Chef, Puppet or Ansible.
No need to overengineer the provisioning script.
The Bash commands in provision.sh
can even be copied and pasted as is in another machine; not Chef, Puppet or Ansible commands.
Flexible configuration
You can add, edit and remove OS packages and repositories from the provision.sh
file.
You can add, edit and remove settings.yaml
values.
The settings.yaml
values feeds Vagrantfile
that builds the virtual machine.
Multiple Vagrant boxes
No need to create one global Vagrantfile
with multiple boxes in it, as explained here: Multi-Machine.
If you clone many boxes in the same parent directory ~/VM
:
mkdir -p ~/VM && cd $_ git clone --depth=1 https://github.com/stemar/vagrant-debian-11.git debian-11 git clone --depth=1 https://github.com/stemar/vagrant-rockylinux-8.git rockylinux-8 git clone --depth=1 https://github.com/stemar/vagrant-ubuntu-20-04.git ubuntu-20-04
~/VM ├── debian-11 │ ├── config │ │ ├── MariaDB.list │ │ ├── adminer.conf │ │ ├── adminer.php │ │ ├── bash_aliases │ │ ├── localhost.conf │ │ ├── php.ini.htaccess │ │ ├── php.list │ │ └── virtualhost.conf │ ├── LICENSE │ ├── README.md │ ├── Vagrantfile │ ├── provision.sh │ └── settings.yaml ├── rockylinux-8 │ ├── config │ │ ├── 00-mpm.conf │ │ ├── MariaDB.repo │ │ ├── adminer.conf │ │ ├── adminer.php │ │ ├── bashrc │ │ ├── gemrc │ │ ├── localhost.conf │ │ ├── localhost.crt │ │ ├── localhost.key │ │ ├── php.ini.htaccess │ │ └── virtualhost.conf │ ├── LICENSE │ ├── README.md │ ├── Vagrantfile │ ├── provision.sh │ └── settings.yaml └── ubuntu-20-04 ├── config │ ├── MariaDB.list │ ├── adminer.conf │ ├── adminer.php │ ├── bash_aliases │ ├── localhost.conf │ ├── php.ini.htaccess │ └── virtualhost.conf ├── LICENSE ├── README.md ├── Vagrantfile ├── provision.sh └── settings.yaml
You can vagrant up
each of them in their own tab in your terminal,
but before they can run concurrently, modify their :host
port in settings.yaml
.
For example:
rockylinux-8 | debian-11 | ubuntu-20-04 | |
---|---|---|---|
SSH | 2200 | 2201 | 2202 |
HTTP | 8000 | 8001 | 8002 |
MySQL | 33060 | 33061 | 33062 |
Each box can be booted or halted, modified, provisioned independently.
When you provision a box for the first time, the .vagrant
directory will be created under its parent.
It will not be created globally for all boxes.
~/VM ... └── ubuntu-20-04 ├── .vagrant ├── config └── ...
Example settings.yaml
:
--- :machine: # https://app.vagrantup.com/bento/boxes/rockylinux-8 :box: bento/rockylinux-8 # 64GB HDD :memory: 3072 # 3GB RAM :cpus: 1 :hostname: rockylinux-8 :timezone: Canada/Pacific :forwarded_ports: # SSH - :id: ssh :host: 2200 :guest: 22 # HTTP - :host: 8000 :guest: 80 # MySQL - :host: 33060 :guest: 3306 :synced_folder: :host: ~/Code :guest: /home/vagrant/Code :copy_files: - :source: ~/.ssh :destination: /home/vagrant/.ssh - :source: ~/.gitconfig :destination: /home/vagrant/.gitconfig :php_error_reporting: E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT # :ruby_version: 3.1.3 ...
settings.yaml
This file feeds values to Vagrantfile
so you should edit both files at the same time.
:machine
:machine
has the values for some Bash settings, virtual hardware, and the box to download.
:forwarded_ports
:forwarded_ports
has arrays of host/guest values:
the :host
value is the port of the physical machine
while the :guest
value is the port of the virtual machine.
To avoid port collision, you must change these port values before running more than one Vagrant box at the same time.
:synced_folder
:synced_folder
has paths of the synchronized directories:
the :host
value is the path of your projects on the physical machine
while the :guest
value is the path of your projects inside the virtual machine.
:copy_files
:copy_files
has arrays of source/destination values:
the :source
value is the path of the file you want to copy from the physical machine
while the :destination
value is the path of the file you want to copy to the virtual machine.
These files are not synchronized; they are copied at provisioning only.
Here we want to clone configuration files so that the same configuration applies on the host and the guest, instead of having different configurations for Git and Subversion in and out of the VM.
:php_error_reporting
:php_error_reporting
is the PHP error reporting value in PHP constants
which feeds the config/php.ini.htaccess
file to display PHP errors on a page in development.
:ruby_version
:ruby_version
is present if the box contains Ruby.
Uncomment the line to install Ruby.
Vagrantfile
This Ruby file builds the Vagrant box from the values of settings.yaml
.
The VM is built from a Bento VirtualBox download.
Some Bash variables are passed to the provision.sh
script.
This page explains the configuration settings: Vagrant Machine Settings
Example Vagrantfile
:
require 'yaml' dir = File.join(File.dirname(File.expand_path(__FILE__))) settings = YAML.load_file("#{dir}/settings.yaml") Vagrant.require_version ">= 2.0.0" Vagrant.configure("2") do |config| config.vm.define settings[:machine][:hostname] config.vm.box = settings[:machine][:box] config.vm.provider "virtualbox" do |vb| vb.name = settings[:machine][:hostname] vb.memory = settings[:machine][:memory] vb.cpus = settings[:machine][:cpus] end config.vm.hostname = settings[:machine][:hostname] settings[:forwarded_ports].each do |port_options| config.vm.network :forwarded_port, **port_options end config.vm.synced_folder settings[:synced_folder][:host], settings[:synced_folder][:guest], owner: "vagrant", group: "vagrant" settings[:copy_files].each do |file_options| config.vm.provision :file, **file_options end unless settings[:copy_files].nil? config.vm.provision :shell, path: "provision.sh", env: { "FORWARDED_PORT_80" => settings[:forwarded_ports].find{|port| port[:guest] == 80}[:host], "GUEST_SYNCED_FOLDER" => settings[:synced_folder][:guest], "PHP_ERROR_REPORTING" => settings[:php_error_reporting], "TIMEZONE" => settings[:machine][:timezone] } end
Example php.ini.htaccess
:
# http://php.net/manual/en/configuration.changes.php # http://php.net/manual/en/ini.list.php # Development environment error settings php_flag display_startup_errors on php_flag display_errors on php_flag html_errors on php_flag ignore_repeated_errors off php_flag ignore_repeated_source off php_flag report_memleaks on php_flag track_errors on php_value docref_root 0 php_value docref_ext 0 php_flag log_errors off php_value log_errors_max_len 0 php_value error_reporting PHP_ERROR_REPORTING_INT # Application settings php_value upload_max_filesize 512M php_value post_max_size 512M php_value memory_limit 512M
php.ini.htaccess
This file will be copied to /var/www/.htaccess
to set php.ini
settings for a development environment.
PHP in this VM is run as an Apache module, not as FastCGI. So we use .htaccess
to set php.ini
settings.
It allows PHP to display errors on the page, turn off error logging, set error_reporting
to the value from settings.yaml
, and set decent memory size values.
virtualhost.conf
You can create DocumentRoot VirtualHost blocks one by one for all the projects you want Apache to serve.
But you could use a VirtualDocumentRoot VirtualHost block to dynamically serve any project that have a common name for their public directory.
In the example on the right:
http://example.com.localhost:8000
is served from itspublic
directory.http://domain.com.localhost:8000
is served from itswww
directory.
The order of all DocumentRoot VirtualHost blocks above a VirtualDocumentRoot VirtualHost block is essential for Apache.
DocumentRoot
Create any number of DocumentRoot VirtualHost blocks in the virtualhost.conf
file with the GUEST_SYNCED_FOLDER
variable for the VM's projects location.
This page explains the DocumentRoot Directive and its options: Apache DocumentRoot Directive
VirtualDocumentRoot
- If you name your projects directory (
GUEST_SYNCED_FOLDER
)/home/vagrant/Code
, and each project is either a domain name or a repository name; you could call the projects directory/home/vagrant/Projects
or any name you like. - If the public directory to be served by the VM's web server is called
www
; you could call itpublic
or any name you like.
~/Code ├── domain.com │ ├── app │ │ └── ... │ ├── vendor │ │ └── ... │ ├── www │ │ └── ... │ └── ... └── app.example.org ├── app │ └── ... ├── bin │ └── ... ├── vendor │ └── ... └── www └── ...
~/Projects ├── bookkeeping_app │ ├── app │ │ └── ... │ ├── bin │ │ └── ... │ ├── config │ │ └── ... │ ├── public │ │ └── ... │ └── ... └── management_app ├── templates │ └── ... ├── vendor │ └── ... └── public └── ...
Using ServerAlias *.localhost
and
VirtualDocumentRoot /home/vagrant/Code/%-2+/www
allow Apache to serve any of the following outside the VM, as long as these projects all have www
as its public directory:
- http://domain.com.localhost:8000
- http://app.example.org.localhost:8000
- http://any.number.of.sub.domains.example.com.localhost:8000
If you want to change the ServerAlias to ServerAlias *.dev
, then:
- http://domain.com.dev:8000
- http://app.example.org.dev:8000
- http://any.number.of.sub.domains.example.com.dev:8000
It's good to have a ServerAlias value of star dot something to avoid confusing the browser's cache to serve a public site vs. a VM local site.
Keeping the standard name *.localhost
makes it clear in the browser's address bar.
Keeping the port visible in the URI is also useful when you have two VMs running and you want to serve two projects from two different ports in two browser tabs.
No need to add entries in /etc/hosts
.
The second directory tree example above would use VirtualDocumentRoot /home/vagrant/Code/%-2+/public
since all its projects use public
as their public directory.
This page explains the VirtualDocumentRoot Directive and its options: Apache Module mod_vhost_alias
Example virtualhost.conf
:
# ------------------ # Using DocumentRoot # ------------------ # https://httpd.apache.org/docs/2.4/mod/core.html#documentroot # # GUEST_SYNCED_FOLDER/ # └── example.com/ # ├── app/ # │ └── ... # └── public/ <= DocumentRoot # └── ... # <VirtualHost *:80> # http://example.com.localhost:FORWARDED_PORT_80 => DocumentRoot ServerName example.com.localhost DocumentRoot GUEST_SYNCED_FOLDER/example.com/public <Directory GUEST_SYNCED_FOLDER/example.com> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog /var/log/httpd/error_log CustomLog /var/log/httpd/access_log combined </VirtualHost> # ------------------------- # Using VirtualDocumentRoot # ------------------------- # https://httpd.apache.org/docs/2.4/mod/mod_vhost_alias.html # # GUEST_SYNCED_FOLDER/ # └── domain.com/ # ├── app/ # │ └── ... # └── www/ <= VirtualDocumentRoot # └── ... # <VirtualHost *:80> # http://domain.com.localhost:FORWARDED_PORT_80 => VirtualDocumentRoot ServerAlias *.localhost VirtualDocumentRoot GUEST_SYNCED_FOLDER/%-2+/www UseCanonicalName Off <Directory GUEST_SYNCED_FOLDER> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog /var/log/httpd/error_log CustomLog /var/log/httpd/access_log combined </VirtualHost>
bashrc | bash_aliases
Add or edit any lines in these files.
Example bash_aliases
(Debian-based OS):
export HISTCONTROL=ignoreboth:erasedups alias ll="ls -lAFh --group-directories-first" alias nano="nano -cw"
Example bashrc
(RHEL-based OS):
export HISTCONTROL=ignoreboth:erasedups export PS1="[\u@\H \w]$ " alias ll="ls -lAFh --group-directories-first" alias nano="nano -cw" # Make rbenv load automatically export RBENV_ROOT="${HOME}/.rbenv" export PATH="${RBENV_ROOT}/bin:${PATH}" eval "$(rbenv init -)"
MariaDB and Adminer
MariaDB and Adminer are both set with no password for username root
so you can avoid writing a password a zillion times through development.
To change the MariaDB version,
go to the Download MariaDB Server page and
create a new repository configuration, then overwrite config/MariaDB.repo
or config/MariaDB.list
depending on the OS.
Adminer is served at http://localhost:8000/adminer.php
. It comes with plugins: login-password-less, dump-json and pretty-json-column.