PostgreSQL 17 on Vagrant and VirtualBox
Create a sandbox to explore how this database is put together
In the article series on an exploratory lab I’ll eventually tackle provisioning of container and virtualization-based sandboxes. To tease at the benefits, we’re going to use a Vagrant-VirtualBox setup to install and poke around a running database.
If you aren’t already using them, you can download and install in order:
Next: Client Connectivity with PostgreSQL
Project files for the article are available from the newsletter’s GitLab repository:
Just create a project that maintains the directory structure. Once you’ve done that and installed both VirtualBox and Vagrant:
cd to the project directory
vagrant up
This will get the ball rolling. If the install will go slowly, it’ll usually be right after generating the SSH key. That tends to happen if there is some difficulty in mapping hardware functionality that the O/S expects to what VirtualBox is able to provide. I’ve found the Ubuntu 22.04 Bento box to work pretty smoothly. It may take 10-20 minutes to download an O/S image, build a VM from it, and install PostgreSQL, and the install will conclude with:
Any future restarts of the box will be much faster. At this point you have:
A running Ubuntu 22.04 VM.
A running PostgreSQL 17 service.
An Ubuntu login account named “vagrant” you can ssh to.
An PostgreSQL user named “vagrant” with the password “vagrant”.
The ability to connect to the database as that user with either a client running within the VM, or from a client on the underlying host operating system.
Let’s take a look at how that came about. The driver of the bus is the Vagrantfile:
This is the file from the GitLab repository but with comments stripped out. Let’s walk through the key parts.
Specify the O/S image you want for the VM:
config.vm.box = "bento/ubuntu-22.04"
config.vm.box_version = "202508.03.0"
PostgreSQL by default listens on port 5432. We want to make it possible for a database client running on the host to connect to the database service within the VM on that same port number:
config.vm.network "forwarded_port", guest: 5432, host: 5432, \
host_ip: "127.0.0.1"
We specify that the provider is VirtualBox, but if you happen to already use Hyper-V or VMware, this is where you would make the change to use them instead:
config.vm.provider "virtualbox" do |vb|
Finally we have a script to run as root for provisioning the VM once it is ready. Ours sets up PostgreSQL and does other assorted housekeeping:
config.vm.provision "shell", name: "postgresql", \
path: "provisions/postgresql.sh", privileged: true
All of the real magic happens in the provisioning script. First we set up the firewall and install some utilities that may come in handy later:
Then we install the APT repository for PostgreSQL, and install the service:
The next step is a bit more packed and needs explaining.
We want to configure the database to have an initial user and database schema to work in. Vagrant allows us to re-run provisioning scripts. Those scripts should be idempotent so they don’t fail they next time they are run. A zero-byte file named .configured is created as a marker to note we’ve already been here before.
The work of the provisioning on the first pass consists of:
Change postgresql.conf to listen for connections coming in from any host. This is done so that it doesn’t just respond to VM clients, it would also respond to the any client the Vagrant configuration allows access to.
Create a user named vagrant, which for simplicity here I’ve made a superuser just like the default user postgres already is.
Provide the vagrant user with a password to allow login. Password-based authentication is not the only option, but we’re going for simple.
Create a database for the new user and give it the same name as that user.
Change pg_hba.conf to allow the new user to have access. We specify both the loopback address to allow login from within the VM, and an address mask suited to any connections over eth0 with IP addresses assigned to the VM or host.
Restart the database service once everything is ready. If this was a production instance I would dig into the docs to remind me which postgresql.conf features require a restart versus a config reload. For here this works fine, and I want a chance to confirm the systemd unit for the service in any case.
Some of the steps are wrapped in a heredoc to run as the postgres user. We are using nested heredocs: the outer level to process steps as postgres, and the inner level to specify content to append to the pg_hba.conf file. The entire provisioning script runs as a privileged user by Vagrant because we said so in the Vagrantfile, which for us means as root. The PostgreSQL service runs as postgres.
Finally, the provisioning script cleans up any dangling APT state. If anything installed flagged a need to reboot the host (in this case, the VM) to complete an installation, then it gets handled now.
We now have the ability to ssh into the running VM. We use vagrant ssh for that, because it knows about the key pair that Vagrant generated when the VM was created.
As a final step, we’ll confirm that the database service processes exist:
In the next article we’ll start exploring the service in more detail.
The Experimentalist : PostgreSQL 17 on Vagrant and VirtualBox © 2025 by Reid M. Pinchback is licensed under CC BY-SA 4.0