DEV Community

Cover image for How To Self-Host Cloudflare
Confidence Okoghenun
Confidence Okoghenun

Posted on • Originally published at confidence.sh

How To Self-Host Cloudflare

Cloudflare offers several products for developers to build modern apps on the edge. Some of my favourites include Pages (an alternative to Netlify—this blog is hosted on Pages), Workers (an alternative to AWS Lamda), KV (an alternative to Redis), and R2 (an alternative to S3). There are many other interesting technologies, and I’m sure you have your picks.

Developers I've interacted with love these products. It's no surprise I keep getting the question ‘how do I host product X or Y on my server?’. I am sharing this article to answer the question and show you how to host Cloudflare’s stack on your infrastructure.

Workerd Runtime

Workerd is a JavaScript & WebAssembly-based runtime that powers Cloudflare Workers and other related technologies. You can think of it like the Node.js runtime used to execute JavaScript files. Workerd has its differences from Node.js, however, you can self-host it on any machine.

Workerd runtime

The rest of this article focuses on containerizing Workerd with Docker. Then I’ll show you how to run a hello-world app in a Workerd container.

Containerizing Workerd

You’ll need Docker installed to follow the steps required to containerize Workerd. If you don’t already have docker installed, you can find a guide to do so here. You’ll also need Node.js installed to run the CLI tools we’ll use to compile the hello-world app later on.

Workerd and Docker

To build a container image for Workerd, we a Dockerfile with the image's recipe. The image includes the latest Workerd binary and glibc (C standard library). Here’s a Dockerfile that builds the specified image:

#build image
FROM ubuntu:latest AS builder

ARG TARGETARCH

WORKDIR /workdir

RUN apt-get update && apt-get install -y curl

#downloads latest workerd release
RUN curl -LO \
    $(curl -s https://api.github.com/repos/cloudflare/workerd/releases/latest \
    | grep -wo "https.*linux-$([ $TARGETARCH = "arm64" ] && echo "arm64" || echo "64").gz")

RUN ls -la && gunzip workerd*.gz && mv workerd* workerd && chmod +x workerd

#copies runtime libraries
RUN mkdir lib && \
    cp /lib/*-linux-gnu/libdl.so.2 lib/libdl.so.2 && \
    cp /lib/*-linux-gnu/librt.so.1 lib/librt.so.1

#container image
FROM busybox:glibc

COPY --from=builder /workdir/workerd /usr/bin/workerd
COPY --from=builder /workdir/lib /usr/lib

WORKDIR /worker

ENTRYPOINT [ "workerd" ]
Enter fullscreen mode Exit fullscreen mode

You’ll note from the Dockerfile that the path /worker is the directory where compiled apps can be served. This is where we’ll put the hello-world (more on this later).

To build the image, run the following command in the Dockerfile directory:

docker build . -t workerd
Enter fullscreen mode Exit fullscreen mode

If the build was successful, a quick docker image ls shows a new image named workerd is created:

$ docker image ls

REPOSITORY               TAG       IMAGE ID       CREATED      SIZE
workerd                  latest    154e4613e479   1 hour ago   140MB
Enter fullscreen mode Exit fullscreen mode

With the image created, you can run local containers and publish the image to Docker hub if you’d like.

Run A Hello-World Container

Workerd in a container

Let’s make a hello-world app to test the image we’ve built. To bootstrap a hello-world app With Wrangler, run the following command:

npm create cloudflare@latest hello-world -- --type=hello-world --ts=false --git=false --deploy=false
Enter fullscreen mode Exit fullscreen mode

Then cd into the project directory i.e hello-world and run this command to compile the app:

npx wrangler deploy --dry-run --outdir .wrangler/dist
Enter fullscreen mode Exit fullscreen mode

The compiled app is emitted to .wrangler/dist/index.js. Now we can create a Workerd container to run the JavaScript code.

But before that, we need to configure how we want Workerd to run. That’s done with a capnp configuration file. Some options include the JS bundle path, the transport protocol, the port to run the service etc.

Create a config.capnp file in the hello-world directory with the following contents:

using Workerd = import "/workerd/workerd.capnp";

const config :Workerd.Config = (
    services = [
        (name = "main", worker = .worker),
    ],
    sockets = [
        (service = "main", name = "http", address = "*:8080", http = ()),
    ]
);

const worker :Workerd.Worker = (
    modules = [
        (name = "worker", esModule = embed ".wrangler/dist/index.js"),
    ],
    compatibilityDate = "2024-02-19",
);
Enter fullscreen mode Exit fullscreen mode

Then run this command in the same directory to serve the hello-world app in a Workerd container:

docker run -d --rm -v $(pwd):/worker -p 8080:8080 workerd serve config.capnp
Enter fullscreen mode Exit fullscreen mode

You can view the app in your browser by visiting http://localhost:8080/.

Hello-world app running

Conclusion

That’s how easy it is to self-host the Cloudflare stack on your own servers. Feel free to use this guide as a base to build your own custom Workerd image. If you’d love to stay up to date with the latest trends in serverless, let’s connect over on Linkedin and Twitter.

Until we meet again on the internet, bye!

Top comments (0)