---
title: "Quick start"
description: "Run a CloudPDF server locally in about a minute, upload a PDF, and see it render — end to end."
source: "https://www.cloudpdf.com/docs/server/getting-started/quick-start"
---

# Quick start

This gets a server running on your machine with **zero external services** —
SQLite, the local disk, and a local cache, all under one volume — then uploads
a real PDF and renders its first page. It's the fastest way to see the server
work end to end.

> Prefer the npm package or a production-grade install? Jump to
> [Deployment](https://www.cloudpdf.com/docs/server/deployment) — the steps below use Docker
> because it needs nothing installed but Docker itself.

## 1. Get a development license key

Every self-hosted server runs on a license key — the server refuses to boot
without one. A **development key** is made for exactly this page: with it, the
server boots with zero further configuration (dev fallbacks, loud warnings,
development-scale limits). How the model works — connected vs air-gapped,
what an expired license does — is on [Licensing](https://www.cloudpdf.com/docs/server/configuration/licensing).

**Get a development key**

Free for local development and evaluation — one click, and it works for your
whole team.

[Request a license key](https://www.cloudpdf.com/contact)

## 2. Run the server

```sh
docker run --rm --init -p 3000:3000 \
  -v cloudpdf-data:/data \
  -e CLOUDPDF_LICENSE_KEY="key/..." \
  -e CLOUDPDF_API_AUTH_TOKENS=dev-api-token-00000000000000000000 \
  -e CLOUDPDF_AUTO_PROVISION_TENANT=1 \
  ghcr.io/embedpdf/cloudpdf-server:latest
```

That's the whole install. The image bakes in everything the server needs —
the Node runtime, native PDFium, image processing, and fonts — so there is no
toolchain to set up.

- `CLOUDPDF_LICENSE_KEY` — your development key from step 1. **Required.**
- `CLOUDPDF_API_AUTH_TOKENS` — a static admin credential for this walkthrough's
  `curl` calls (any long random string).
- `CLOUDPDF_AUTO_PROVISION_TENANT=1` — creates tenants on first use. Dev
  convenience; leave it off in production.
- `-v cloudpdf-data:/data` keeps your documents across restarts; `--init`
  gives the container clean signal handling.

> A development key deliberately permits insecure dev fallbacks (like the
> built-in JWT secret) so this one command works. Production keys refuse
> them: you'll set a real `CLOUDPDF_JWT_SECRET` and secrets of at
> least 32 bytes — see
> [Authentication](https://www.cloudpdf.com/docs/server/configuration/authentication).

## 3. Confirm it's healthy

```sh
curl localhost:3000/healthz
# {"status":"ok"}

curl localhost:3000/readyz
# {"license":{...},"status":"ok"}
```

`/healthz` means the process is alive; `/readyz` means it can serve (the
database answers, not draining). Both are public — everything else requires a
credential:

```sh
curl -i localhost:3000/v1/tenants/default/documents
# HTTP/1.1 401 Unauthorized
```

A `401` here is the server working correctly: it refuses unauthenticated
requests.

## 4. Upload a PDF and render it

Uploads are a three-step protocol — announce, send bytes, commit — because in
production the bytes go **straight to your object store** on a presigned URL
and never pass through the API. Locally the server proxies them, and the SDKs
wrap all three steps in one call; here it's three `curl`s:

```sh
API_TOKEN=dev-api-token-00000000000000000000
PDF=./my-document.pdf
SHA=$(shasum -a 256 "$PDF" | cut -d' ' -f1)

# 1. announce the upload
DOC=$(curl -s -X POST localhost:3000/v1/tenants/default/documents/init \
  -H "Authorization: Bearer $API_TOKEN" -H 'content-type: application/json' \
  -d "{\"contentLength\":$(wc -c < "$PDF"),\"contentSha256\":\"$SHA\",\"uploadPreference\":\"proxy\"}" \
  | sed -n 's/.*"id":"\([^"]*\)".*/\1/p' | head -1)

# 2. send the bytes
curl -s -X POST "localhost:3000/v1/tenants/default/documents/$DOC/upload-proxy" \
  -H "Authorization: Bearer $API_TOKEN" \
  -F "file=@$PDF;type=application/pdf" > /dev/null

# 3. commit (the server verifies the SHA-256)
curl -s -X POST "localhost:3000/v1/tenants/default/documents/$DOC/commit" \
  -H "Authorization: Bearer $API_TOKEN" -H 'content-type: application/json' \
  -d "{\"sha256\":\"$SHA\"}"
```

Now render its first page:

```sh
curl -s "localhost:3000/v1/tenants/default/documents/$DOC/thumbnail" \
  -H "Authorization: Bearer $API_TOKEN" -o first-page.webp && open first-page.webp
```

That WebP came out of the same render pipeline your users will hit — native
PDFium, rendered on demand and cached as an immutable artifact.

## 5. Talk to it from your app

Your frontend uses [`@cloudpdf/engine`](https://www.cloudpdf.com/docs/engine), pointed at the server
you just started:

```ts
import { createCloudEngine } from '@cloudpdf/engine';

const engine = createCloudEngine({
  baseUrl: 'http://localhost:3000',
  token: () => getDocToken(), // doc-scoped JWT minted by your backend
});

const doc = await engine.open({ kind: 'id', id: docId });
const page = doc.page(1);
const image = await page.render.image({ viewport: { kind: 'width', width: 1200 } });
```

Your backend mints the `getDocToken()` JWT — see
[Authentication](https://www.cloudpdf.com/docs/server/configuration/authentication) for the token
shape (and the `signDevToken` helper for local experiments), and the
[engine docs](https://www.cloudpdf.com/docs/engine/getting-started/quick-start) for rendering a page
into the DOM.

## 6. Stop it

```sh
docker stop $(docker ps -q --filter ancestor=ghcr.io/embedpdf/cloudpdf-server:latest)
```

Your data stays in the `cloudpdf-data` volume, ready for the next run.

## Where to go next

You ran the simplest possible configuration. To take it further:

- [Licensing](https://www.cloudpdf.com/docs/server/configuration/licensing) — Connected vs air-gapped, key kinds, and what a lapsed license does.
- [Choose a deployment](https://www.cloudpdf.com/docs/server/deployment) — npm, Docker, Compose, or Helm — and the trade-offs of each.
- [Move to Postgres + S3](https://www.cloudpdf.com/docs/server/configuration/database) — Swap SQLite and local disk for production-grade storage.
- [Authentication](https://www.cloudpdf.com/docs/server/configuration/authentication) — Mint document-scoped tokens from your backend.
