---
title: "Deploy with content files"
description: "Ship the content folder with your server and keep reading it from the filesystem."
canonical_url: "https://content.comark.dev/deployment/with-content-files"
---
# Deploy with content files

> Ship the content folder with your server and keep reading it from the filesystem.

When your host runs a Node process and deploys your whole project, the simplest deployment is no change at all: the `content/` folder ships with the code, and `fs('./content')` keeps reading it. This page covers the two things that can go wrong, and how to check.

This path fits a VPS, a container, a platform that builds from your repository and runs `node`, and any setup where your build output sits next to the source tree. If your host bundles only the files your code imports, such as most serverless platforms, the folder won't be there; use [a snapshot](https://content.comark.dev/deployment/with-a-snapshot) instead.

## Make sure the folder ships

Build outputs don't include `content/`. `nuxt build` writes `.output/`, `next build` writes `.next/`, and neither copies a folder your code reads at runtime rather than imports. The folder is still in your repository, so it's present when you deploy the repository and run the server from its root.

In a container, copy it explicitly:

```dockerfile [Dockerfile]
COPY .output ./.output
COPY content ./content
CMD ["node", ".output/server/index.mjs"]
```

With Next.js and `output: 'standalone'`, the standalone folder is meant to be self-contained, so tell the tracer to include the content:

```ts [next.config.ts]
const nextConfig = {
  output: 'standalone',
  outputFileTracingIncludes: {
    '/**/*': ['./content/**/*'],
  },
}
```

## Resolve the path reliably

`fs('./content')` resolves against the directory the process starts in. That's your project root in development, and it's whatever your start command uses in production. Keep the two the same, or pass an absolute path:

```ts [content.ts]
import { fileURLToPath } from 'node:url'

export const content = comarkContent({
  source: fs(process.env.CONTENT_DIR ?? fileURLToPath(new URL('./content', import.meta.url))),
})
```

`import.meta.url` works when the built file keeps its position relative to `content/`. Bundlers that move server code into `.output/server/chunks/` break that assumption, which is why the environment variable is the safer of the two for Nuxt and Nitro. Their guides also show a third option: bundling the folder as [server assets](https://content.comark.dev/integrations/nitro#deploy), which makes the build output self-contained.

## Verify

Build, then start the built server from a shell whose working directory matches production, and read one page:

```bash [Terminal]
pnpm build
node .output/server/index.mjs &
curl -s http://localhost:3000/api/content/get/about | head -c 200
```

If the response is `null` or a 404, the instance found no files: check the working directory and the path you passed to `fs()`.

## Updating content

Content changes ship the way code changes do: commit, deploy, restart. The new process reads the files as they are. If you'd rather update content without a deploy, move it to a [remote source](https://content.comark.dev/deployment/remote-content).

Don't rely on [`content.watch()`](https://content.comark.dev/guide/watch) in production. It's designed for a developer editing files on the same machine, not for a deployment pipeline.


## Sitemap

See the full [sitemap](https://content.comark.dev/sitemap.md) for all pages.
