Crazy Idea to Postgres in the Browser

Joey Mezzacappa

5 min read

We just launched our Postgres Playground. Running Postgres in the web browser was not exactly commonplace before, so naturally, people are wondering how it works.

It actually started as a fun weekend experiment. Here's a screenshot I saved, just moments after recovering from the initial "whoa, it's working!" effect.

Screenshot of PostgreSQL running in the Chrome web browser

The next morning, I shared this screenshot in our internal Slack channel for web frontend engineering. Our mental gears began to turn as we imagined what might (and might not) be possible. After a bit of work, we built upon some interesting ideas, and this fun weekend hack evolved into what is now the Postgres Playground!

This blog post focuses on how to run PostgreSQL in the web browser, but there are other interesting pieces involved in the playground as well. For example, the content in the tutorials actually lives in Notion documents. There may be a blog post about that in the near future from one of my colleagues, so stay tuned!

Why?

I stumbled upon an interesting blog post about how Wasmer has a "Run in Playground" link for some Markdown fenced code blocks for a few of their WAPM packages. There was one in particular that really got my attention: SQLite. On that page, there is a fenced code block with some SQL queries inside. If you click the "Run in Playground" button, it runs the query right there in the web browser with SQLite compiled to WebAssembly.

After running that SQLite query in my browser, I thought, "Can I do this with Postgres?".

The modern web browser is a very powerful platform, and this platform's capabilities are constantly increasing. However, WebAssembly still has some growing to do in some areas. After some quick research, I found that the web browser simply does not offer the networking features that Postgres needs. That would seem like a pretty big obstacle.

However...

As I mentioned, the modern web browser is a very powerful platform. Let's just change the target platform to something other than WebAssembly, then run it in WebAssembly anyway like a rebel. 😎

Virtual machines in the browser

It's actually possible to emulate a PC inside the web browser! There have been quite a few implementations over the years. Some even started out in JavaScript, years before WebAssembly was an option. Here are a few that I found especially interesting:

Emulator Architecture
Halfix x86
JSLinux x86 and RISC-V
jor1k OpenRISC 1000
v86 x86
WebVM x86

I ended up choosing v86 for this. The author started the project in 2011, and it's still active. Many questions have been answered in the GitHub issues and discussions over the years. I'm definitely not an expert in Linux or virtualization, so being able to search the repo for answers was very helpful.

v86's performance also seemed to be among the best, compared to similar open source emulators that run in the browser. In early 2021, they merged a Rust port + JIT into the master branch, which provided a significant performance boost over the original JavaScript implementation.

Build

For this blog post, we'll use Alpine Linux. It's a lightweight Linux distribution and a very popular base for many Docker images. They also have a version with a slimmed-down kernel, optimized for virtual machines.

Install Alpine Linux

Note: You'll need to have QEMU installed.

Download the Alpine image that is optimized for VMs. We'll need the x86 (not x86_64) build.

wget https://dl-cdn.alpinelinux.org/alpine/v3.16/releases/x86/alpine-virt-3.16.0-x86.iso

Create a disk image for the VM. I chose my disk size somewhat randomly, so feel free to make it larger if you'd like. You could probably make it smaller, but I'm not sure what the minimum size limit would be here.

qemu-img create alpine.img 512M

Start the VM

qemu-system-x86_64 \
  -m 256M \
  -cdrom alpine-virt-3.16.0-x86.iso \
  -drive file=alpine.img,format=raw

Note: Depending on your host machine, you may be able to get a performance boost by appending another argument to the command:

Host machine Argument
Linux with x86 CPU -accel kvm
macOS with x86 (Intel) CPU -accel hvf
macOS with Apple Silicon None, but your CPU is a beast anyway.

When the VM has finished booting, log in as 'root'.

Run setup-alpine. For most of the setup questions, you can configure things to your liking, but these are important:

Question Answer
Which disk(s) would you like to use? sda
How would you like to use it? sys
WARNING: Erase the above disk(s) and continue? y

After the installation has finished, use the reboot command to reboot the VM from the virtual hard disk image.

Install Postgres

After the VM has rebooted, log in as root again. We can now install and initialize Postgres.

apk add postgresql --no-cache
/etc/init.d/postgresql setup
/etc/init.d/postgresql start
rc-update add postgresql

Now, a quick smoke test:

su - postgres -c 'psql -c "SELECT version();"'

If it worked, you should see something like this:

                                                    version
----------------------------------------------------------------------------------------------------------------
 PostgreSQL 14.4 on i586-alpine-linux-musl, compiled by gcc (Alpine 11.2.1_git20220219) 11.2.1 20220219, 32-bit
(1 row)

Note: On my machine, psql opened the version information in less, so I had to press q to exit that.

Now we can shut down the VM.

poweroff

Run the VM in the web browser

  1. Go to v86's site.
  2. Scroll down to the "Setup" section.
  3. For "Hard disk drive image", select the alpine.img file you created earlier.
  4. Adjust "Memory size" to your liking. (I used 256 MB)
  5. Click the "Start Emulation" button.

Note: It will take a bit longer for the VM to boot. Running the VM in the web browser will not be as fast as it was in QEMU.

After it has finished booting, log in as root, then open psql:

su - postgres -c psql

Congratulations! You are now running Postgres in your web browser.

Things to keep in mind:

  • There is no internet access from inside the VM.
  • There is no data persistence, so changes are lost when leaving or refreshing the page.
Joey Mezzacappa

Written by

Joey Mezzacappa

August 24, 2022 More by this author