Skip to content

Getting started

View markdown

Run KDIGI CMS on your machine in a few minutes, log in, and create your first site.

PathYou need
Docker Compose (easiest)Docker with the Compose plugin
pnpm dev (for development)Node.js ≥ 20.9 (22 recommended), pnpm 10 (corepack enable), Docker for PostgreSQL (or your own PostgreSQL; the compose files use 16)
Terminal window
git clone https://github.com/kdigivn/kdigi-cms-open.git
cd kdigi-cms-open
SEED_DEMO=true docker compose up --build

Open http://localhost:3000/admin. On a fresh database the admin shows a "Create first user" form; the first user you create becomes the super-admin. Drop SEED_DEMO=true if you want an empty install.

What the stack does (docker-compose.yml):

  • starts PostgreSQL 16 (db service) and builds/starts the app (app service);
  • on every app start the entrypoint (docker/entrypoint.sh) generates a PAYLOAD_SECRET on first start if you did not set one (kept in the app_data volume), applies database migrations (payload migrate), optionally loads the demo content (SEED_DEMO=true), then runs next start;
  • keeps data in named volumes: postgres_data (database), media_data (uploads, served by the app), app_data (generated secret). docker compose down keeps them; docker compose down -v deletes everything.

Useful switches (set in your shell or in a .env file next to the compose file):

VariableDefaultEffect
APP_PORT3000host port of the CMS
DB_PORT5432host port of PostgreSQL (bound to 127.0.0.1 only)
POSTGRES_PASSWORDkdigidatabase password — local use only; change it if you expose the port
PAYLOAD_PUBLIC_SERVER_URLhttp://localhost:3000public URL of the CMS; set it if you change APP_PORT or put the stack behind a domain
SEED_DEMOemptytrue loads the demo site and sample content (idempotent)

All other settings are described in configuration.

Terminal window
git clone https://github.com/kdigivn/kdigi-cms-open.git
cd kdigi-cms-open
pnpm install --frozen-lockfile
cp .env.example .env # the defaults match `docker compose up db`; set PAYLOAD_SECRET
docker compose up -d db # only PostgreSQL, on localhost:5432
pnpm migrate # create the database schema (explicit; push is disabled)
pnpm seed:demo # optional: demo site + sample content
pnpm dev # http://localhost:3000/admin

Notes:

  • Replace PAYLOAD_SECRET in .env with a random string of at least 32 characters (openssl rand -hex 32).
  • The schema is never auto-synchronised: after pulling new code run pnpm migrate again (upgrading).
  • If port 3000 is busy Next.js picks 3001; development accepts the localhost origins 3000, 3001 and 3101, so login keeps working.
  • Without a running database the app cannot start; use your own PostgreSQL by editing DATABASE_URI in .env.
  1. Log in with the first user you created. Forgot the password in a local install? Reset or create a super-admin from the command line:
    Terminal window
    node --env-file=.env --import tsx src/scripts/create-admin-user.ts you@example.com 'a-long-password'
    (with Docker: docker compose exec app node --import tsx src/scripts/create-admin-user.ts you@example.com 'a-long-password').
  2. Create a site (Sites → Create new). A site is one website: set a unique siteKey (your frontend uses it to ask for its content), the bare domain (for example example.com) and the default language. The demo seed already creates a site with siteKey demo. See multi-site.
  3. Pick the site in the admin site selector, then write content: posts, pages, categories/tags, menus, FAQs. The editor is Markdown-first (content-markdown).
  4. Upload media. Files go to local disk by default (media-storage); no cloud account is needed.
  5. Invite people (Users) and assign them a role and one or more sites (roles and permissions). Only super-admin can create users.
  6. Connect a frontend. Your website fetches published content over REST or GraphQL, for example:
    Terminal window
    curl 'http://localhost:3000/api/posts?where[tenant][equals]=1&sort=-publishedAt&limit=5'
    Anonymous requests only see published content, and tenant is the numeric id of your site (look it up by siteKey via /api/sites?where[siteKey][equals]=demo). See REST and GraphQL API for the exact contract (site selection, locale, SEO fields, revalidation).

Use the importer: WordPress migration.