Deploying Decentralized Frontends with Oyster: A step-by-step guide

Marlin Oyster is an off-chain compute marketplace that allows frontends and backends to be deployed on a decentralized node network that is secured by enclaves. DAOs, dApps and Web 3 projects looking to host tamper-proof decentralized UIs can do so with Oyster. Your users will never know the difference

This isn’t restricted to just frontends either. Any off-chain process can be outsourced to the Oyster network. If you’d like to learn more about the tech behind Oyster, check out our Pearls of Wisdom thread. We've also covered Oyster's use cases in our Oyster Menu thread

In this blog, we’ll be taking you through the steps of deploying your frontend on Oyster using a simple static website. If you get held up at any step in the guide or require support, please reach out on Discord. Let’s get started.


Before we start, ensure you have the following prerequisites in place:

  1. Docker
  2. Optional: Tool to publish enclave image publicly (more in step 3)

Take note of the CPU architecture of the Oyster enclave you want to deploy. For this tutorial, the CPU Architecture is assumed to be amd64. If the architecture is arm64, replace all instances of amd64 with arm64.

Step 1: Configuring Caddy

In this section, we’ll be preparing the artifacts necessary to deploy a frontend and create an enclave image that can be run on Oyster using the Image Builder tool. The Oyster Image Builder is a docker image that simplifies the process of creating images for Oyster enclaves. The Oyster Image builder features Caddy as its default choice for automating the acquisition and management of TLS certificates. This ensures that your website is equipped with HTTPS which is essential for security. 

Let’s start by creating a static file and configuring caddy to serve it. Create a static HTML file named index.html on your local file system in the /home/oyster/website directory, with the following contents:

<!DOCTYPE html>
        <title>Hello from Oyster</title>
        <h1 style="font-family: sans-serif"> Welcome to Decentralized website powered by Oyster</h1>

Next, let’s configure Caddy. Create a file named Caddyfile in the /home/oyster directory and input the following lines:

} {
    root * /app/mount/website

Let’s break this down. We’re designating as the domain for serving static pages. Caddy’s static file server capability will manage file delivery, with the root set to /app/mount/website instead of the previous /home/oyster/website. This is because /home/oyster is mounted to the enclave builder Docker image at /app/mount.

/home/oyster on local file system == /app/mount on enclave image builder

We suggest restricting ACME CA to Let’s Encrypt as it is the only Certificate Authority (CA) we know of that implements RFC 8657 which is necessary to secure tamper-proof websites using Oyster.

Step 2: Building the enclave image

In this section, we’ll be creating an enclave image that can be run by Oyster.

Create a config.json file at /home/oyster with the following contents:

    "caddy": {
        "url": "",
        "caddyfile": "Caddyfile"
    "volume": "/app/mount",
    "params": {
        "arch": "amd64"
    "service_commands": []

Now, let’s use the config.json to build the enclave image using:

cd /home/oyster
docker run -it --privileged --env TARGETARCH=amd64 -v /home/oyster:/app/mount marlinorg/enclave-builder .

The output will be in the below format:

  "Measurements": {
    "HashAlgorithm": "Sha384 { ... }",
    "PCR0": "00d1df767988fcf57fdb8c1d495b55f1c01cededc14e311e223d38c52bf4e1e972e88b0098c1d040dcab50bb41306f6d",
    "PCR1": "5d3938eb05288e20a981038b1861062ff4174884968a39aee5982b312894e60561883576cc7381d1a7d05b809936bd16",
    "PCR2": "12d499d809e13635c2a225003e72b1394abbbd239ae134049028987b4456d4d2d44cc5b31ea4586580fb70a32a6b1a80"

Make a note of the PCR values (PCR0, PCR1, PCR2) from the output as we’ll be using these later to verify the enclave’s data.

With the command executed successfully, the enclave image will be available at /home/oyster/enclave/enclave.eif.

Step 3: Uploading the enclave image

To deploy the enclave, a URL that hosts the enclave image which is accessible by the deployer has to be provided. To create a URL, you can use options like IPFS, self-hosted images, or local images with a URL that’s accessible to the public through tools like ngrok, or localtunnel. In this tutorial, we will use the approach of building the images locally and using to create a publicly accessible URL.

First, set up a local static server at /home/oyster/enclave using Python:

cd /home/oyster/enclave
python -m http.server 8000

Now, let’s use to create a publicly available URL by running:

ssh -R 80:localhost:8000 [email protected]

The result provides a publicly accessible URL for the enclave image that looks like https://<prefix>

Step 4: Deploying the enclave from the UI

Once you have your enclave image URL ready, the enclave can be deployed using Oyster’s UI by following this guide. Ensure that the CPU architecture of the deployed enclave is as specified in step 2. Make a note of the CPU and Memory details of the deployed enclave as they will be used to verify if the enclave was actually deployed with those specifications in step 5.

The IP address of the enclave can be obtained from Oyster UI as mentioned here (refer to the Managing your servers section).

Step 5: Set DNS records

Let’s dive into the final step of your decentralized website which is configuring DNS records. These include A record and CAA (Certification Authority Authorization) records, which together bind the enclave to the domain and are critical in securing your decentralized website.

We’ll do this by extracting the  CAA records from the enclave, and verifying them for integrity before setting them in DNS.

The caa-extractor utility can be used to extract and verify the integrity of the CAA records extracted from the enclave. Use the following to download it and setup:

cd /home/oyster
wget -o caa-extractor
chmod +x caa-extractor

Execute the caa-extractor tool to verify the enclave is based on correct image represented by PCR values, it has correct CPU and memory values and extract CAA records for both production and staging environments. Utilize the PCR values from Step 2, cpu and memory values and the enclave’s IP address from Step 4.

We will use CAA records for both staging and production versions of Let’s encrypt Certificate Authority(CA) to ensure that certificate generation once CAA records are set can recover from any failures. This is because caddy fallsback to staging if it faces any failures when generating certificates and only retries if staging certificate is correctly generated.

./caa-extractor --ca --enclave-ip <enclave-ip> -0 <PCR0> -1 <PCR1> -2 <PCR2> -c <CPUs> -m <Memory> 
./caa-extractor --ca --enclave-ip <enclave-ip> -0 <PCR0> -1 <PCR1> -2 <PCR2> -c <CPUs> -m <Memory>

Set the CAA records for both prod and staging environments as per the output of the above commands along with A record that points to the IP address from step 4.

It might take a few minutes for the DNS records to propagate. Once complete, Caddy will recognize the updates, and your decentralized website will be ready to go.

And that’s about it. Reach out to us on Discord for support, to report bugs and/or to suggest improvements. 

Follow our official social media channels to get the latest updates as and when they come out!

Twitter | Telegram Announcements | Telegram Chat | Discord | Website

Stay connected

Subscribe to our newsletter.