Native Golang Support for Workers

First off, I think the idea and core concept of Workers is awesome. There is so much potential from the security and performance aspect.

I’m no server-side JS expert. So I’ve tried to become more acquainted with the engine, tried to setup a simple but also performant, maintainable and scalable project that could also be used as an App. The experiences I made were rather disappointing. There are some barriers that make developing no fun.

Here are the reasons:

1. Lack of documentation and engine limitations

Initially, I assumed that either frontend code or Node JS code would be fully compatible. However, Workers use the Chrome V8 engine and limit various functions. eval doesn’t work, fetch is available, but it is unclear how it works internally. When is which part streamed, when is it buffered? Afaik, there is no complete documentation or library you could refer to. If you write everything manually without libraries, that might not be a problem. However, if you use libraries, we come to the next problem.

2. Building with JS/Webpack is too heterogeneous and complex

I’m rather keen to use or publish libraries than writing everything on my own or copy-pasting snippets. In my view, the separation of concerns supports the security and maintenance of each concern, and product respectively. With Workers, you will need to do one of both if you don’t want to buffer everything and use Regex. I.e., either use libraries (e.g., for streaming, DOM) or copy/paste since chunks are arbitrarily split and may change encodings.

So what’s the problem? What kind of libraries are offered? Essentially, most libraries either support frontend engines, Node JS, or both. Workers, however, have limited compatibility with these libraries, especially streaming-focused libraries. Node libraries often use fs or net. Frontend ones expect DOMs packages to be available. Ultimately, the most frustrating compatibility issue for me was the streaming package and NodeJS readable-stream. Even though Webpack should be capable of transforming it, afaik, it doesn’t transform pipe for me. Rewriting libraries to make them compatible is possible but pointless.

The overall overhead from Webpack is huge. The build configuration is a lot of trial and error. There are libraries that solely push your code beyond the size limit of 1MB. Minifying/Compression is a workaround; the code becomes more difficult to debug, then. Looking forward to the release of Apps with Workers, which requires a code review, I have no idea how that is supposed to work with Webpack code.

What’s the solution?

I might be a little bit biased in this question, but I have worked with various web languages and frameworks. Golang seems to be an appropriate choice as an alternative language to JS. Besides the performance benefits and Golang’s service orientation, it also solves the beforementioned problems. Golang is simple, has little to no overhead when using the standard libraries. The net/http package makes streaming and modifications of web traffic straight-forward. There is no confusion with API coverage. Libraries can only be incompatible if they are unmaintained; there is no “frontend Golang”; there is only one compiler per version. Compilation could be done server-sided, no post-processing required (such as with Webpack). Dependencies are transparently defined with their full path and versions in every file. One file is sufficient, even if libraries are used. There is a native testing library that can be used for complete net/http tests.

All in all, what I’m trying to say is that I hope for a simpler development solution without the need to trade in either maintainability or performance. Golang would be one approach.


Disappointing, but thank you for letting us know that there is currently no plan.

Regarding the processing isolation, I’m wondering if you have considered Golang’s actor framework Golang offers ideal conditions for isolation, even without the need to create “real” threads. Actors further extend that model by using routers, so there is no need to create one thread/goroutine per service/worker/request or whatsoever. The amount of routers can be scaled with the available processing/hardware resources. Even Docker is implemented in Go.

1 Like

You could build a WebAssembly module using Go and only have a small JS wrapper. AFAIK WebAssembly modules are slower than plain JS in many cases in Cloudflare Workers, because data has to be copied back and forth, but I’m not sure what the overhead is.

1 Like

@johanna Indeed, it is not only giving up Golang’s performance benefit, it also exceeds the 1MB. Minifying might help, but then again, there is this pointless post-processing.

Not sure what you mean by exceeding size. Cloudflare has posted examples in the past of building WebAssembly modules with Rust and C and running them in workers eg Serverless Rust with Cloudflare Workers

Although I can’t say what the limit on WebAssembly modules is or how small the output is when you compare go and rust builds.

1 Like

@johanna I mean the worker size limit of 1MB as discussed in this post. Doesn’t that limit apply on wasm files also?

I mean, they support WebAssembly and they’ve posted about building them using eg Rust and running it. It doesn’t make sense that they would do that if you can’t upload modules.

But now I’m curious about the size so I made it a separate thread here What is the maximum allowed size of a WebAssembly module?

Hi @matfax,

Unfortunately, Go fundamentally is not designed to support secure sandboxing of tenants without running each tenant in their own process. Workers runs every customer’s code in every one of Cloudflare’s locations, which means we need an extremely efficient way to host lots and lots of apps on one machine. Giving each app its own process would be far too expensive to consider.

The only language runtime that is really designed for secure sandboxing within a process is JavaScript, because sandboxing has always been extremely important in browsers.

That said, we’re working on improving WebAssembly to enable support for more languages. At present, it works OK for C/C++/Rust. Unfortunately, Go is a challenge to get working on Workers right now due to requiring a large code footprint (garbage collector, green threads, etc.) and wanting to allocate too much memory. We’re working on solutions for these problems, such as allowing multiple WASM apps to share a single copy of a common runtime, but this is a complicated project that will take time.


Thank you for clarifying. I see the problem with the sandboxing. But you are working on it. :+1:

In the meanwhile, I might try setting up a build using GopherJS. I’ve heard a lot of positive reviews because it doesn’t have the performance and footprint trade-offs of WebAssembly. The only question is how to make it compatible; if and how it works with the available interfaces, similar to the abovementioned JS libraries.

1 Like

The cloudflare worker settings page on my dashboard says Go can be run on the edge:

Is this possible and/or supported now? Are there examples available?

1 Like

Possible via WASM, but generally your go application needs to be created specifically with Workers in mind (and I’m only vaguely familiar with WASM, I couldn’t go into much detail)

1 Like

Which URL are you seeing this at? Mine doesn’t have “Go” in it.

1 Like

Looks like “Go” was removed from that page.

I found an example here:

That blog post looks interesting, but I can’t figure out out to make it useful using the particular tools the author used, because when using TinyGo I can only pass in and return singular numerical values between js and golang, otherwise it errors out.

I have a feeling the answer is shared memory but having a difficult time figuring out how it’s supposed to work.

Got it working with callbacks and the stock go compiler. The worker will just return the url as response:

//+ build js,wasm

package main

import (

func test(_ js.Value, input []js.Value) interface{} {
	return nil

func main() {
	js.Global().Set("test", js.FuncOf(test))
	<-make(chan interface{})

And the js


addEventListener('fetch', (event) => {

function handleRequest(req) {
  return new Promise((async (resolve, reject) => {
    try {
      const go = new Go();

      const instance = await WebAssembly.instantiate(WASM, go.importObject);;

      test(req.url, (answer) => {
        resolve(new Response(answer, { status: 200 }));
    } catch (e) {
      reject(new Response(e.message, { status: 500 }));
1 Like