PostgreSQL DBA Lab: Build the VM with Vagrant, Add Disks and Create LVM

PostgreSQL DBA Lab: Build the VM with Vagrant

Table of Contents



Prerequisites

Install these on your Windows laptop:

  • Oracle VirtualBox
  • Vagrant
  • PuTTY (to log in to the VM)
  • WinSCP (optional, to copy the LVM script into the VM)

Open a Command Prompt and confirm the tools work:

C:\> vagrant --version
C:\> "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" --version

Note: Free disk space on the laptop matters. The disks are dynamic, so they start small and grow as data is written.

Lab Setup

ItemValue
VM namepg-dba-01
Hostnamepgdba01.lab.local
IP address (host-only)192.168.2.201
Operating systemOracle Linux 9 (Vagrant box oraclelinux/9)
Memory / CPU3072 MB / 1 vCPU
Loginroot / oracle
Vagrant folderC:\VM\PGDBA
VM files folderC:\VM\PGDBA
Disk files folderC:\VM\PGDBA\pg-dba-01

Disk and mount point layout:

Mount pointDisk sizeVolume groupLogical volume
/pgdata100 GBvg_pgsql_pgdatalv_pgsql_pgdata
/tsdata100 GBvg_pgsql_tsdatalv_pgsql_tsdata
/pgbackup200 GB (pgdata + tsdata)vg_pgsql_pgbackuplv_pgsql_pgbackup
/pgarch100 GBvg_pgsql_pgarchlv_pgsql_pgarch
/pglogs50 GBvg_pgsql_pglogslv_pgsql_pglogs

Sizing rule: /pgbackup is the size of /pgdata plus /tsdata, so it can hold one full copy of both.

Step 1: Build the VM with Vagrant

Create the folder C:\VM\PGDBA and save the following as a file named Vagrantfile (no extension). Only the five highlighted lines at the top need editing.

# ===== Edit these 5 lines =====
VM_NAME     = "pg-dba-01"
VM_HOSTNAME = "pgdba01.lab.local"
VM_IP       = "192.168.2.201"
VM_MEMORY   = 3072
VM_CPUS     = 1
# ==============================

Vagrant.configure("2") do |config|
  config.vm.box = "oraclelinux/9"
  config.vm.box_url = "https://oracle.github.io/vagrant-projects/boxes/oraclelinux/9.json"
  config.vm.box_check_update = false
  config.vm.synced_folder ".", "/vagrant", disabled: true

  config.vm.network "private_network", ip: VM_IP, auto_config: false
  config.vm.hostname = VM_HOSTNAME

  config.vm.provider "virtualbox" do |vb|
    vb.name   = VM_NAME
    vb.memory = VM_MEMORY
    vb.cpus   = VM_CPUS
  end

  config.vm.provision "shell",
    env: { "VM_IP" => VM_IP, "VM_HOSTNAME" => VM_HOSTNAME },
    inline: <<~'SHELL'
    set -e

    echo "== 1. RHCK kernel as default =="
    dnf install -y kernel lvm2 xfsprogs
    RHCK=$(ls /boot/vmlinuz-* | grep -v uek | grep -v rescue | sort -V | tail -1)
    grubby --set-default="$RHCK"

    echo "== 2. Hostname and host-only IP =="
    hostnamectl set-hostname "$VM_HOSTNAME"
    NAT_IF=$(ip route show default | awk '{print $5}' | head -1)
    HO_IF=$(ip -o link show | awk -F': ' '{print $2}' | grep -v -E "^(lo|${NAT_IF})$" | head -1)
    nmcli -g NAME con show | grep -x hostonly >/dev/null 2>&1 && nmcli con delete hostonly
    nmcli con add type ethernet ifname "$HO_IF" con-name hostonly \
      ipv4.method manual ipv4.addresses ${VM_IP}/24 ipv6.method ignore \
      connection.autoconnect yes
    nmcli con up hostonly
    grep -q "$VM_IP" /etc/hosts || echo "$VM_IP $VM_HOSTNAME ${VM_HOSTNAME%%.*}" >> /etc/hosts

    echo "== 3. Root login and password (Putty: 192.168.2.201 : root/oracle)=="
    echo "root:oracle" | chpasswd
    cat > /etc/ssh/sshd_config.d/00-lab.conf <<'EOF'
PermitRootLogin yes
PasswordAuthentication yes
EOF
    sshd -t
    systemctl restart sshd
  SHELL
end

What the provisioning script does:

  • Installs the RHCK kernel, lvm2 and xfsprogs, and makes RHCK the default kernel.
  • Sets the hostname and gives the second (host-only) network adapter your static IP.
  • Sets the root password to oracle and allows root login over SSH, so PuTTY works.
C:\> cd "C:\Program Files\Oracle\VirtualBox"
C:\Program Files\Oracle\VirtualBox> VBoxManage setproperty machinefolder "C:\VM\PGDBA"

Note: machinefolder changes the default VM folder of VirtualBox for your whole laptop, not only for this lab. Vagrant has no setting for the VM location, so this is the simplest way.

Build the VM:

C:\> cd C:\VM\PGDBA
C:\VM\PGDBA> vagrant up

The first run downloads the Oracle Linux 9 box, so it takes a few minutes. When it finishes, the VM is running with hostname and IP already configured.

Step 2: Log in and Shut Down

Before connecting, check the host-only adapter on Windows. PuTTY can only reach the VM if your laptop has an IP address in the same network (192.168.2.x). Open:

WIN+R -- ncpa.cpl
— OR —

Control Panel\All Control Panel Items\Network and Sharing Center
   --> Change adapter settings
   --> right-click VirtualBox Host-Only Ethernet Adapter --> Properties
   --> select Internet Protocol Version 4 (TCP/IPv4) --> Properties

Select Use the following IP address and enter:

FieldValue
IP address192.168.2.1
Subnet mask255.255.255.0
Default gateway / DNSLeave empty

Click OK, then check from a Command Prompt that the VM answers:

C:\> ipconfig
C:\> ping 192.168.2.201

Tip: Shortcut to the same screen: press Win + R, type ncpa.cpl and press Enter. If the adapter already shows 192.168.2.1, no change is needed. Use any free address in the VM’s network, but never the VM’s own IP (192.168.2.201).

Open PuTTY and connect with these values:

PuTTY fieldValue
Host Name192.168.2.201
Port22
Login / passwordroot / oracle

Check the hostname and the IP address, then shut the VM down so we can add disks:

[root@pgdba01 ~]# hostname
pgdba01.lab.local
[root@pgdba01 ~]# ip -4 addr show | grep 192.168.2
    inet 192.168.2.201/24 brd 192.168.2.255 scope global noprefixroute eth1
[root@pgdba01 ~]# shutdown -h now

Step 3: Add 5 Dynamic Disks

Disks can only be attached while the VM is powered off.

C:\> cd "C:\Program Files\Oracle\VirtualBox"
C:\Program Files\Oracle\VirtualBox> VBoxManage list runningvms

Check the name of the storage controller. The default for this box is SATA Controller:

C:\Program Files\Oracle\VirtualBox> VBoxManage showvminfo pg-dba-01 | findstr /i "controller"

3.1 Create the disks. The size is in MB. --variant Standard means the disk is dynamically allocated.

VBoxManage createmedium disk --filename "C:\VM\PGDBA\pg-dba-01\pgdata.vdi"   --size 102400 --format VDI --variant Standard
VBoxManage createmedium disk --filename "C:\VM\PGDBA\pg-dba-01\tsdata.vdi"   --size 102400 --format VDI --variant Standard
VBoxManage createmedium disk --filename "C:\VM\PGDBA\pg-dba-01\pgbackup.vdi" --size 204800 --format VDI --variant Standard
VBoxManage createmedium disk --filename "C:\VM\PGDBA\pg-dba-01\pgarch.vdi"   --size 102400 --format VDI --variant Standard
VBoxManage createmedium disk --filename "C:\VM\PGDBA\pg-dba-01\pglogs.vdi"   --size 51200  --format VDI --variant Standard
Disk fileSize in GBSize in MB
pgdata.vdi100102400
tsdata.vdi100102400
pgbackup.vdi200204800
pgarch.vdi100102400
pglogs.vdi5051200

3.2 Attach the disks. Ports 1 to 5 are used because port 0 is the OS disk.

VBoxManage storageattach pg-dba-01 --storagectl "SATA Controller" --port 1 --device 0 --type hdd --medium "C:\VM\PGDBA\pg-dba-01\pgdata.vdi"
VBoxManage storageattach pg-dba-01 --storagectl "SATA Controller" --port 2 --device 0 --type hdd --medium "C:\VM\PGDBA\pg-dba-01\tsdata.vdi"
VBoxManage storageattach pg-dba-01 --storagectl "SATA Controller" --port 3 --device 0 --type hdd --medium "C:\VM\PGDBA\pg-dba-01\pgbackup.vdi"
VBoxManage storageattach pg-dba-01 --storagectl "SATA Controller" --port 4 --device 0 --type hdd --medium "C:\VM\PGDBA\pg-dba-01\pgarch.vdi"
VBoxManage storageattach pg-dba-01 --storagectl "SATA Controller" --port 5 --device 0 --type hdd --medium "C:\VM\PGDBA\pg-dba-01\pglogs.vdi"

Confirm that all five disks are attached:

C:\Program Files\Oracle\VirtualBox> VBoxManage showvminfo pg-dba-01 | findstr /i ".vdi"

Note: Keep the order exactly as shown. Linux names the disks sdb to sdf in port order, and the LVM script in Step 5 uses them in the same order. Replace pg-dba-01 with your VM name and C:\VM\PGDBA\pg-dba-01 with your own folder.

Step 4: Start the VM and Check the Disks

C:\> cd "C:\Program Files\Oracle\VirtualBox"
C:\Program Files\Oracle\VirtualBox> VBoxManage startvm "pg-dba-01"

Log in with PuTTY again and list the disks:

[root@pgdba01 ~]# lsblk -d -o NAME,SIZE,TYPE
NAME   SIZE TYPE
sda     37G disk
sdb    100G disk
sdc    100G disk
sdd    200G disk
sde    100G disk
sdf     50G disk

Step 5: Create VG, LV and Mount Points

Create the script inside the VM with vi /root/lvm-setup.sh and paste the content below.

#!/usr/bin/env bash
# Run inside the VM as root:  bash lvm-setup.sh
# One disk -> one VG -> one LV -> one mount point.
set -euo pipefail
[ "$EUID" -eq 0 ] || { echo "Run as root"; exit 1; }

# ===== Edit here =====
FS_TYPE="xfs"
# name : expected disk size in GB : mount point   (disks are used in this order: sdb, sdc, sdd ...)
# VG = vg_pgsql_<name>, LV = lv_pgsql_<name>
VOLS=(
  "pgdata:100:/pgdata"
  "tsdata:100:/tsdata"
  "pgbackup:200:/pgbackup"
  "pgarch:100:/pgarch"
  "pglogs:50:/pglogs"
)
# =====================

# Collect empty disks (no partitions, no filesystem, not LVM yet)
FREE_DISKS=()
for d in $(lsblk -dnpo NAME,TYPE | awk '$2=="disk"{print $1}' | sort); do
  if [ "$(lsblk -n "$d" | wc -l)" -eq 1 ] && ! blkid "$d" >/dev/null 2>&1 && ! pvs "$d" >/dev/null 2>&1; then
    FREE_DISKS+=("$d")
  fi
done
echo "Empty disks found: ${FREE_DISKS[*]:-none}"
[ "${#FREE_DISKS[@]}" -ge "${#VOLS[@]}" ] || { echo "Need ${#VOLS[@]} empty disks, found ${#FREE_DISKS[@]}"; exit 1; }

i=0
for v in "${VOLS[@]}"; do
  IFS=: read -r name gb mp <<< "$v"
  disk="${FREE_DISKS[$i]}"; i=$((i+1))
  vg="vg_pgsql_$name"; lv="lv_pgsql_$name"; dev="/dev/$vg/$lv"

  actual=$(( $(lsblk -bdn -o SIZE "$disk") / 1073741824 ))
  if [ "$actual" -ne "$gb" ]; then
    echo "ERROR: $disk is ${actual}G but $name expects ${gb}G. Check disk order (lsblk) or edit VOLS."; exit 1
  fi

  echo "== $name: $disk (${gb}G) -> $vg/$lv -> $mp"
  pvcreate -y "$disk"
  vgcreate "$vg" "$disk"
  lvcreate -y -l 100%FREE -n "$lv" "$vg"
  mkfs."$FS_TYPE" "$dev"
  mkdir -p "$mp"
  grep -qs "^$dev " /etc/fstab || echo "$dev $mp $FS_TYPE defaults,nofail 0 0" >> /etc/fstab
done

systemctl daemon-reload
mount -a
if id postgres >/dev/null 2>&1; then
  for v in "${VOLS[@]}"; do IFS=: read -r _ _ mp <<< "$v"; chown postgres:postgres "$mp"; done
fi

echo; vgs; lvs; df -hT | grep -E "pg|tsdata"

Run it:

[root@pgdba01 ~]# bash /root/lvm-setup.sh

What the script does for each disk:

  • pvcreate turns the disk into an LVM physical volume.
  • vgcreate creates the volume group vg_pgsql_<name> on it.
  • lvcreate -l 100%FREE creates the logical volume lv_pgsql_<name> using the whole disk.
  • mkfs.xfs formats it, then the script creates the mount point and adds a line to /etc/fstab so it mounts after every reboot.

Step 6: Verify the Result

[root@pgdba01 ~]# vgs
[root@pgdba01 ~]# lvs
[root@pgdba01 ~]# df -hT | grep -E "pg|tsdata"

You should see five volume groups, five logical volumes and five mounted XFS file systems, similar to this (sizes are slightly below the disk size because of LVM and file system overhead):

/dev/mapper/vg_pgsql_pgdata-lv_pgsql_pgdata     xfs    100G  746M  100G   1% /pgdata
/dev/mapper/vg_pgsql_tsdata-lv_pgsql_tsdata     xfs    100G  746M  100G   1% /tsdata
/dev/mapper/vg_pgsql_pgbackup-lv_pgsql_pgbackup xfs    200G  1.5G  199G   1% /pgbackup
/dev/mapper/vg_pgsql_pgarch-lv_pgsql_pgarch     xfs    100G  746M  100G   1% /pgarch
/dev/mapper/vg_pgsql_pglogs-lv_pgsql_pglogs     xfs     50G  389M   50G   1% /pglogs

Test that the mounts survive a reboot:

[root@pgdba01 ~]# reboot
# log in again
[root@pgdba01 ~]# df -hT | grep -E "pg|tsdata"

Cleanup

Remove the lab when you are finished:

C:\VM\PGDBA> vagrant destroy -f

Then delete the five .vdi files in C:\VM\PGDBA\pg-dba-01.

Warning: vagrant destroy deletes the VM and everything stored on it. The .vdi files stay on your disk until you delete them yourself.

Caution: Your use of any information or materials on this website is entirely at your own risk. It is provided for educational purposes only. It has been tested internally, however, we do not guarantee that it will work for you. Ensure that you run it in your test environment before using.
Thank you
Rajasekhar Amudala