Running Perl/PAGI applications on Cloudflare Workers via WebAssembly

Background

I've been developing the WebDyne framework to make creation of Perl-based web applications easier (for me anyway). Most of the apps I write now use John Napiorkowski's excellent PAGI design, as it supports modern asynchronous web services such as SSE and WebSockets.

One of the challenges has always been: "where do I host them?"

They run fine in Docker containers, and the demo website currently runs on Sevalla in a relatively cheap container. I could also run them in an AWS Lambda environment, but with either option I have to pick a region, and — especially with full Docker containers — hosting multiple sites can be relatively expensive for "small" applications.

Cloudflare Workers and WASM

Cloudflare Workers have always looked attractive. For a starting price of $5/month on the compute and storage plans you get a fair bit of "bang for your buck", with about 8 CPU-hours/month for Worker isolates (basically Lambda functions), with the added bonus that they are — or at least can be — "Cloud Edge": they spin up close to the consumer on demand, but you are charged for actual usage rather than the number of Workers that are running.

Obviously prices go up with consumption, but you can get a fair bit done with the starting price.

Couple that with access to storage services such as D1 (SQLite-compatible database), KV storage and R2 (S3-compatible object storage), and things start looking reasonably attractive.

But, like almost everywhere today, Perl is a second-class citizen. Cloudflare Workers primarily target JavaScript, TypeScript and Python, with languages such as Rust supported through WebAssembly.

But importantly, Workers can run WebAssembly (WASM).

Enter zeroperl

So, WebAssembly. How does that work with Perl?

Back in 2025 Andrew Sampson debuted zeroperl via a blog post. It was a port of Perl to WebAssembly.

So maybe that could be adapted to support Perl with services such as Cloudflare Workers?

Long story short, that's what I have attempted to do.

I have made some modifications to the original zeroperl to add PAGI support and added a harness which lets it run as a Cloudflare Worker.

The resulting Worker loads a persistent Perl interpreter compiled to WASM, then bridges Cloudflare's JavaScript request/event model into Perl. HTTP requests are presented to Perl as PAGI $scope, $receive and $send interfaces, which means existing PAGI applications can run with relatively little or no modification.

The same bridge also supports asynchronous PAGI features such as WebSockets.

Demos

Demo, you say?

Firstly, before you look at them, let's get the AI bit out of the way. WebDyne is human written, with a few AI bug fixes. The demo apps below are about half and half — I'm pretty crap at UI design, so the Perl code is mostly human but they are skinned by AI.

With that out of the way, some demos:

  • Firstly, a vanilla PAGI app, a basic "Hello World" example that dynamically returns the Cloudflare Worker server time. The GitHub repo contains the source.

  • If you just want to see the basics and how to get started very simply with a WebDyne::PAGI app, have a look at Perl Fortune. The GitHub repo gives simple instructions on how to build, test and deploy it.

  • A more fully fledged demo showcases WebSocket support: terminal-based emulation allowing you to play the Scott Adams text adventures through a browser. Play it here, with the GitHub repo here.

  • A mock Wine Cellar demo talks to Cloudflare D1 and also uses KV and R2 object storage. The GitHub repo is here.


More info and limitations

I've attempted to make the instructions as clear as possible, but feedback and bug reports are welcome.

The demos attempt to showcase some of the features of WebDyne, including support for htmx, so that there is minimal JavaScript — HTML is swapped in and out rather than built via JavaScript — and the ability for Perl calls to return data as embedded JSON, which can then be read by JavaScript charting libraries etc.

There are limitations.

Pure-Perl CPAN modules are relatively straightforward to bundle; XS modules generally need to be compiled specifically for WASM or replaced with Pure-Perl equivalents. This isn't intended to make every existing Perl application magically run unchanged on Workers.

I've packaged the build/runtime tooling as @webdyne/webdyne-zeroperl on npm, with some sister packages for Cloudflare such as @webdyne/webdyne-cloudflare.

The idea is that an application repository contains the Perl application and its dependencies, while the packages handle building the WASM bundle and the Cloudflare/Wrangler integration.

Cloudflare does offer a free tier that supports Workers, but the limitations on size and startup time make it impractical to fit the current zeroperl/WebDyne bundle within the free-tier limits.

What about just running Perl in the browser?

And one more, not entirely serious, demonstration.

If we've gone to the trouble of compiling Perl and WebDyne to WASM, there's no particular reason the WASM has to execute inside a Cloudflare Worker. A browser can instantiate it too.

Might as well cut out the middle man and just download zeroperl to the browser and run the WASM right on the user's machine to serve HTML, right?

So, if you are prepared to download 4+ MB of WASM, you can run a WebDyne app directly from this GitHub Pages demo. The GitHub repo is here.

I don't see a lot of demand for doing it — but hey, soon 4 MB might count as a small download.

Links

Some more links:

Leave a comment

About Andrew Speer

user-pic I blog about Perl.