Requirements
The server, database, storage and provider accounts Adcraft needs, why serverless hosting is not enough, and a server sizing starting point.
4 min readUpdated Oct 17, 2026Applies to Adcraft 1.0.0
On this page
At a glance
| You need | Required for | Notes |
|---|---|---|
| Node.js 22 or newer | Everything | The Docker image is built on node:22-bookworm-slim. |
| pnpm 9.15.9 | Building from source | Pinned in package.json; corepack enable installs the right version. |
| A long-running process | Everything | A VPS, a container host or a platform that keeps one process alive. Not serverless functions. |
| PostgreSQL | Production | Any Postgres you can reach over TCP. Without one, an embedded database is used for local evaluation. |
| Durable file storage | Production | A mounted volume, or a Cloudflare R2 bucket. Generated images and videos live here. |
| A domain with HTTPS | Production | Sign-in links and OAuth callbacks return to AUTH_URL, which should be your HTTPS address. |
| A way to sign in | Production | Resend (magic links) or Google sign-in. The development sign-in box is switched off in production. |
| AI provider keys | Real generation | None are required to start. Each key switches on a capability; see AI models and providers. |
| Stripe account | Optional | Only to charge customers for plans and credits. |
| Inngest account | Optional | Moves background jobs to Inngest Cloud and adds the hourly performance sync. |
| Meta, TikTok or Google Ads developer apps | Optional | Only for publishing to real ad accounts. Without them, built-in sandbox accounts simulate campaigns. |
Why it needs an always-on process
Three things in Adcraft do not fit a serverless function:
- Video rendering. Product videos are assembled with Remotion, which drives a headless Chromium. Presenter (UGC) videos are assembled with FFmpeg. A render can take minutes.
- Website import. Reading a brand from a website can open a headless browser.
- Inline jobs. Without Inngest, generation runs inside the web process and keeps working after the page that started it has returned.
Container platforms (Docker on a VPS, Railway, Render, Fly.io) and a plain VM all work. Shared PHP hosting and cPanel do not: Adcraft is a Node.js application.
Warning
Turn off any "scale to zero" or auto-stop feature on your host. A stopped container cannot finish a render that was already running.
Database
- PostgreSQL. The included Docker image installs the PostgreSQL 18 client for the nightly backup, and
pg_dumpmust match your server's major version. Postgres 18 is the safe choice if you want the built-in backup; see Backups. - Any provider. Self-hosted, Neon, Supabase, Railway and similar all work. The app connects with the
postgresdriver over TCP. - Migrations run automatically when the Docker container starts, or by hand with
pnpm --filter @adcraft/db db:migrate. See Installation.
For a first look you can skip Postgres entirely. With no DATABASE_URL, the app starts an embedded Postgres (PGlite) that keeps its files under .data/pglite in the project folder.
Important
The embedded database is for local evaluation only. In production, set DATABASE_URL. If you run the Docker image without it, you must set PGLITE_DATA_DIR to a writable, mounted volume, because the application folder inside the image is read-only for the app user.
File storage
Uploads (logos, product photos, footage) and every generated image and video are written to storage:
- Local disk by default:
LOCAL_STORAGE_DIR, which the Docker image sets to/data/files. Mount a volume there. - Cloudflare R2 when
R2_ACCOUNT_ID,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEYandR2_BUCKETare all set.
A container's own filesystem is wiped on every redeploy. Without a volume or R2, every generated asset disappears with it.
Provider accounts
None of these are needed to try the product: without keys, text concepts return labelled sample output and ad platforms run in sandbox mode. Real generation needs the matching key.
| Provider | What it powers |
|---|---|
| Anthropic | Ad concepts, copy, scripts and brief help (Claude) |
| fal.ai | The default image and video models, and background removal |
| OpenAI, xAI | Alternative image models and text models |
| HeyGen | AI presenters, avatars, digital twins, looks and the HeyGen voice library |
| ElevenLabs | Voice-over for presenter videos |
| Replicate, Runway | Optional extra models, shipped disabled |
| Resend | Magic-link sign-in, invitations and "your video is ready" emails |
| Stripe | Plans, credit top-ups and the customer portal |
| Inngest | Background jobs with retries, and scheduled tasks |
AI models and providers maps every key to the features it switches on.
Server size
These numbers are guidance, not a measured benchmark. Video rendering is the heaviest workload by far.
| Workload | Suggested starting size |
|---|---|
| Evaluation, static ads only | 1 vCPU, 1 GB RAM |
| Rendering product or presenter video on the same machine | 2 vCPU, 2 GB RAM or more |
| A SaaS with several customers rendering video | Start at 4 GB RAM, watch memory during renders, and scale up before scaling out |
Start with one instance. Without Inngest, jobs run inside the process that started them, and the generation rate limits are kept in memory per process.
Third-party licences
Read THIRD-PARTY-NOTICES.md in the package before commercial use. In short:
- Remotion, used only for product-video rendering, is free for individuals and organisations of up to three people. Larger organisations need a company licence from Remotion. See Remotion's licence.
- FFmpeg, used for presenter video, is downloaded at install time by
ffmpeg-staticunder GPL terms. You can pointFFMPEG_PATHat your own build instead. - Satori and resvg, used for static ads, are MPL-2.0 and place no obligation on your own code.
Local development
For a laptop install you only need Node.js 22 and pnpm. Postgres, keys and a domain can all come later. Quick start walks through it.