nitro icon indicating copy to clipboard operation
nitro copied to clipboard

Cloudflare Containers, durable objects

Open oritwoen opened this issue 6 months ago • 4 comments

Describe the feature

I would like to be able to use Cloudflare Containers in a project with Nitro. Configuring wrangler from own file or nuxt.config.ts works fine. I can make use images and bindings.

The problem is that the export must be in the main bundle and not chunks, hence the error:

Your Worker depends on the following Durable Objects, which are not exported in your entrypoint file: MyContainer.

This is generally not a problem of Containers itself. Generally every durable_object binding returns an error after building because it is not in the main bundle.

There is also an issue here: https://github.com/nitrojs/nitro/issues/3378

In the cloudflare_durable preset there is something like this: https://github.com/nitrojs/nitro/blob/v3/src/presets/cloudflare/runtime/cloudflare-durable.ts#L60

and it exports 1 binding, durable object with name $DurableObject, and with containers it needs to export somewhere in the route something like below. And while there is no error during build, there is an error during deploy because this export is not in the main bundle.

import { Container, getContainer } from "@cloudflare/containers";

export class MyContainer extends Container {
  defaultPort = 4000; // Port the container is listening on
  sleepAfter = "10m"; // Stop the instance if requests not sent for 10 minutes
}

Additional information

  • [ ] Would you be willing to help implement this feature?

oritwoen avatar Jun 30 '25 09:06 oritwoen

Any update on this? Did anyone got it working somehow?

valtlfelipe avatar Sep 02 '25 13:09 valtlfelipe

I found a solution 🎉

I created a custom preset that extends the cloudflare default.

nitro: {
    preset: "./preset",
}

Preset:

// preset/nitro.config.ts
import type { NitroPreset } from "nitropack";
import { fileURLToPath } from "node:url";

export default <NitroPreset>{
    extends: "cloudflare_module",
    entry: fileURLToPath(new URL("./entry.ts", import.meta.url)),
};

// preset/entry.ts
import "#nitro-internal-pollyfills";
import { Container } from "@cloudflare/containers";
import cloudflareModule from "nitropack/presets/cloudflare/runtime/cloudflare-module";

// could be also a Durable Object
export class MyContainer extends Container {
    defaultPort = 3000;
    sleepAfter = '5m';

    override onStart() {
        console.log('Container successfully started');
    }
    override onStop() {
        console.log('Container successfully shut down');
    }
    override onError(error: unknown) {
        console.log('Container error:', error);
    }
}

export default cloudflareModule

Then it will be inside the cloudflare context:

const container = event?.context?.cloudflare?.env?.MY_CONTAINER

valtlfelipe avatar Sep 02 '25 19:09 valtlfelipe

Thanks @valtlfelipe! Looks like a very clean workaround, will be very useful. I wouldn't have thought of that solution.

oritwoen avatar Sep 02 '25 19:09 oritwoen

@pi0 maybe it's worth adding to the documentation an example of extending the preset for such non-standard situations and new platform features that are not yet natively supported by the framework as in the example above?

oritwoen avatar Sep 02 '25 19:09 oritwoen