Kwisple
Browse the docs

Your site repository › Going live

Hosting and deploys

Connect a site repository in the dashboard, choose between Cloudflare, your own host with a deploy hook or the platform's own server, and control what happens on publish, on redeploy and for pull-request previews.

A custom-code site is built from its own repository. Content never needs a deploy: publishing refreshes a running site through its webhook (Live preview and revalidation). Only code changes, and static sites, need a build. This page covers where a site can run, how to connect its repository to the dashboard, and what the dashboard does with it afterwards.

OptionWho runs the buildWhat the platform doesTypical site
CloudflareCloudflare Pages, from GitHubCreates the project, sets the variables, attaches domains, starts and follows deploys, lists pull-request previews.Static or hybrid sites.
Eigen host (deploy hook) (own host)Your host (Vercel, Netlify, Cloudflare), from GitHubCalls your deploy hook, optionally reads a status address, records branch previews from a URL pattern.Any host you already use.
The platform's own serverA container next to the platform, built from the site repositoryServes the site on the client's domain behind HTTPS; the dashboard gives the DNS records.Server-rendered Next.js (RANSON.N).
Testhost (lokaal) (test host)NobodySimulates builds for development of the platform itself.Only when NODE_ENV is not production or HOSTING_FAKE=1.

Connecting a repository

Who can do this: the agency team, with the deploy right (site.deploy). Only custom-code sites can be connected ("Enkel websites met eigen code kan je aan een repository koppelen.").

  1. Push the site to GitHub.
  2. Open the website and go to Instellingen (settings) → Ontwikkeling (development) → Hosting & deploys.
  3. Click Koppel een repository (connect a repository). A sheet opens with three steps: Host, Repository, Bevestigen (confirm). Move with Volgende (next) and Terug (back).
  4. Host. Under Waar draait de website? (where does the website run?) pick Cloudflare or Eigen host (deploy hook). Cloudflare only appears when the platform has Cloudflare credentials.
  5. Repository.
    • Repository op GitHub as owner/name (for a deploy hook it is Repository (optioneel)). A pasted https://github.com/… prefix is accepted.
    • Hoofdbranch (main branch), default main: "Pushes hierop gaan live."
    • Cloudflare: Gebouwd met (built with) Astro, Next.js or Iets anders (something else).
    • Deploy hook: Deploy hook (the hook URL, stored encrypted, never shown again) and Adres van de website (the address the host gives the site, such as https://acme.vercel.app). Under Geavanceerd (advanced): Adres van voorbeelden (preview address pattern) and Status-adres (optioneel).
  6. Bevestigen. Check host, repository and main branch, choose Als inhoud verandert (when content changes): Alleen verversen (only refresh) or Opnieuw bouwen (rebuild), see Deploy on publish. Click Koppelen (connect).

After connecting, the first production deploy starts ("Gekoppeld. De eerste deploy loopt."). Warnings, such as a domain that could not be attached, appear in the message. The platform also fills in what the site does not have yet, and never overwrites what you set: the site's Site-URL from the production address, and with Alleen verversen its Webhook bij publiceren as <production address>/api/revalidate.

Cloudflare

With Cloudflare the platform drives Cloudflare Pages through its API:

  • creates a Pages project named after the site's slug, with the GitHub repository as source, the main branch as production branch and pull-request comments and previews on;
  • build settings per framework: Next.js npx @cloudflare/next-on-pages@1 with output .vercel/output/static; Astro and others npm run build with output dist (a static build: an on-demand Astro site needs an adapter that suits Pages, so check that your site builds with these settings);
  • creates a fresh site token with read + preview named Hosting · Cloudflare and sets KWISPLE_URL, KWISPLE_TOKEN, KWISPLE_WEBHOOK_SECRET, KWISPLE_DASHBOARD_ORIGIN, KWISPLE_ASSET_ORIGIN and KWISPLE_SITE for production and previews (the token and the secret as secrets);
  • attaches the site's domains, and every domain you add later; the domain screen then gives a CNAME to the Pages address (Domains and DNS);
  • starts the first deploy, reads deploy states, and imports deployments Cloudflare started itself from git pushes and pull requests (<branch>.<project>.pages.dev).

Needs, once: CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN (permission Cloudflare Pages: Edit) in the platform's environment, and the Cloudflare Pages GitHub app installed on the repository's account or organisation (Cloudflare dashboard → Workers & Pages → Connect to Git); the API cannot do that step. The credentials never leave the platform's server and are scrubbed from error messages. The Cloudflare provider is written against the documented API; test the first connection with a site you can afford to rebuild.

If a step fails while connecting, the half-made Pages project is deleted again.

Eigen host (deploy hook)

You keep your host as it is: it builds from GitHub itself. The platform only POSTs {} to the deploy hook to start a production build (for content rebuilds and Opnieuw deployen). Find the hook in Vercel (Settings → Git → Deploy Hooks), Netlify (Build hooks) or Cloudflare Pages.

FieldMeaning
Deploy hookRequired. Encrypted at rest, never sent back to the browser. A hook that redirects is refused ("De deploy hook stuurt door; gebruik de eindbestemming.").
Adres van de websiteWhere production is served before a custom domain.
Adres van voorbeeldenA preview URL pattern with {branch}, such as https://{branch}--acme.netlify.app. {branch} becomes a host-safe alias (lowercase, other characters as -, at most 28 characters); {branch_raw} and {sha} are also filled in.
Status-adres (optioneel)An address that returns JSON about the latest deploy. Without it a started build shows Aangevraagd (requested). Encrypted like the hook.

The status address may answer any JSON with state (Netlify), readyState or a deployments list (Vercel), result.latest_stage (Cloudflare) or a plain { "status", "url" }. Values such as ready, success, live map to Online; building, queued, in_progress to Bezig; error, failed to Mislukt; canceled, skipped to Geannuleerd.

You set the KWISPLE_* variables at your host yourself: the same names kwisple link writes (Environment variables and tokens), with a production token without the schema scope. The Geavanceerd panel lists them under Variabelen van de site.

The platform's own server

A client site can also run on the platform's own VM, as its own container next to the platform, behind the same Caddy that serves the dashboard and the API. RANSON.N runs this way. This is set up by whoever operates the platform; there is no hosting connection in the dashboard for it, and code updates are deployed with a script on the server.

The site repository: a Dockerfile

RANSON.N's Dockerfile is the template for a Next.js site:

# syntax=docker/dockerfile:1.7
FROM node:22-bookworm-slim AS build
RUN corepack enable
WORKDIR /site
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY vendor ./vendor
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY . .
ARG KWISPLE_ASSET_ORIGIN
ENV NEXT_OUTPUT=standalone KWISPLE_ASSET_ORIGIN=${KWISPLE_ASSET_ORIGIN}
RUN --mount=type=secret,id=site_env,required=true \
    sh -c 'set -a && . /run/secrets/site_env && set +a && pnpm build'

FROM node:22-bookworm-slim
ENV NODE_ENV=production HOSTNAME=0.0.0.0 PORT=4103
WORKDIR /app
USER node
COPY --from=build --chown=node /site/.next/standalone ./
COPY --from=build --chown=node /site/.next/static ./.next/static
COPY --from=build --chown=node /site/public ./public
EXPOSE 4103
CMD ["node", "server.js"]

(The real file pins an exact Node image and sets PNPM_HOME and telemetry variables.) Three things matter:

  • next.config.ts sets output: "standalone" only when NEXT_OUTPUT=standalone, so pnpm start keeps working locally.
  • The build prerenders the shell (layout, 404) from the live delivery API, so it needs the site's variables. They come in as a BuildKit secret (--secret id=site_env): the token never ends up in an image layer. Build after the platform is up.
  • KWISPLE_ASSET_ORIGIN is a build argument because next/image reads the image host when the config loads. Changing it means rebuilding.

.dockerignore keeps node_modules, .next, .git and every .env* except .env.example out of the build context. To build by hand:

docker build --secret id=site_env,src=.env.production \
  --build-arg KWISPLE_ASSET_ORIGIN=https://assets.example.com -t acme-website .

The compose service

The site is one service in the platform's infra/docker-compose.prod.yml, checked out next to the platform repository, behind the compose profile sites so a plain docker compose up -d never touches it:

site-ranson-n:
  profiles: ["sites"]
  restart: unless-stopped
  env_file:
    - { path: ../../kwisple-ranson-n/.env.production, required: false }
  build:
    context: ../../kwisple-ranson-n
    args:
      KWISPLE_ASSET_ORIGIN: ${KWISPLE_ASSET_ORIGIN:?missing in .env.production}
    secrets:
      - source: site_ranson_n_env
        target: site_env

The same .env.production of the site serves as runtime environment and as build secret (declared under top-level secrets:). A host block in infra/Caddyfile routes the client's domain to the container's port with automatic HTTPS, adds default security headers when the site sends none, and redirects the other spelling (with or without www) to the main address. Adding the next site is one more service and one more host block, copied from site-ranson-n.

Going live on the server

  1. In the dashboard, under Instellingen → Ontwikkeling, create a site token (read + preview) and note the Webhook-geheim.
  2. On the server, write the site's .env.production with KWISPLE_URL, KWISPLE_TOKEN, KWISPLE_WEBHOOK_SECRET, KWISPLE_ASSET_ORIGIN and KWISPLE_DASHBOARD_ORIGIN.
  3. Set the site's Site-URL, Webhook bij publiceren (https://<domain>/api/revalidate) and Preview-URL (https://<domain>/api/preview) before the image is built: canonical links, Open Graph, JSON-LD, sitemap and robots bake the address in.
  4. Under Instellingen → Algemeen → Domein choose Op de server van het platform and add the domain (Domains and DNS).
  5. Build and start the site: tools/go-live/deploy.sh site <name>.
  6. Point DNS at the server (DNS only, no proxy), check the records, and set the site Online zetten (set online).

Deploying code updates

tools/go-live/deploy.sh site ranson-n            # pull ../kwisple-ranson-n, build, restart
tools/go-live/deploy.sh site ranson-n staging    # the staging worktree (profile "staging")
tools/go-live/deploy.sh status                   # containers, memory, disk

deploy.sh site <name> runs git pull --ff-only in ../kwisple-<name>, builds and restarts the service site-<name> with the profile sites, and prunes old images. With staging it uses ../kwisple-<name>-staging and the service site-<name>-staging (Staging). deploy.sh platform updates the platform only and never rebuilds client sites.

Deploy on publish

Als inhoud verandert decides what a publish does to a connected site. Change it later under Hosting & deploys → Geavanceerd.

ChoiceForOn publish
Alleen verversenServer-rendered sites (Next.js ISR, Astro on demand)The signed webhook purges the site's cache. No build. The panel reads "Publiceren ververst de website meteen, zonder opnieuw te bouwen."
Opnieuw bouwenStatic sitesA new production build, at most one per minute ("Publiceren bouwt de website opnieuw (hoogstens één keer per minuut).").

How a rebuild is scheduled: a publish adds one waiting deploy with the trigger Na publiceren (after publishing) and enqueues the site.deploy job with a per-site singleton key and a 60-second window. Publishes inside the window share that one build; a publish after a build always gets a new one. The build is never started inside the publish request: it needs the platform's worker (or inline jobs in development).

Static sites without a connected repository can still rebuild on publish by pointing Webhook bij publiceren at the host's deploy hook.

Following deploys

Once connected, Hosting & deploys shows:

  • a status pill with Bezig (building), Online, Mislukt (failed), Geannuleerd (cancelled), Aangevraagd (requested) or Nog niet gedeployed (not deployed yet), with the time; the panel polls every 3 seconds while something builds;
  • the host, repository, main branch and production address;
  • Recente deploys (recent deploys): the last production deploys with commit, branch and trigger (Na publiceren, Handmatig (manual), Bij koppelen (on connect), Vanuit git (from git));
  • Opnieuw deployen (redeploy): starts a production build of the main branch now ("Nieuwe deploy gestart.").

A refused deploy (an unreachable hook, an error at Cloudflare) is recorded as Mislukt with a reason that never contains credentials. The website overview shows the last deploy to the team.

Previews of branches and pull requests

The Voorbeelden (previews) list shows one address per branch or pull request, the newest per branch. Cloudflare builds them from every pull request; the platform imports them with the trigger Vanuit git. For a deploy hook, add one under Geavanceerd → Voorbeeld van een branch (branch preview): type the branch and click Toevoegen; the address comes from Adres van voorbeelden ("Stel eerst het adres van voorbeelden in (Geavanceerd)." when it is empty). On Cloudflare the button is Bouwen and starts a real preview build.

Ready previews appear in the entry editor's live preview under Voorbeeld op, so an editor can see draft content on a branch's code. Each preview deployment needs the site's KWISPLE_* variables (Cloudflare sets them for previews too) and the /api/preview route at the same path as the site's Preview-URL.

Advanced settings

Hosting & deploys → Geavanceerd holds:

  • Als inhoud verandert, Hoofdbranch (on Cloudflare a new branch is set on the project too), Adres van de website (bij de host) and Adres van voorbeelden; for a deploy hook a new Deploy hook or Status-adres (empty keeps the stored one), then Opslaan;
  • Voorbeeld van een branch;
  • Variabelen van de site: the variable names, set by the platform (Cloudflare) or by you;
  • Contentmodel in CI with CI-token maken: a read + schema token for the starter's workflow, shown once (CI and automation);
  • Ontkoppelen (disconnect): removes the connection and its deploy history and revokes the hosting token; the site stays online at the host. On Cloudflare you can also tick Ook het project bij Cloudflare verwijderen (de website gaat dan offline).

Secrets

Deploy hooks and status addresses are encrypted at rest (AES-256-GCM with HOSTING_SECRET_KEY, or a key derived from BETTER_AUTH_SECRET) and never sent back to the browser. Hooks and status addresses must be https and public in production; redirects are refused. Tokens the platform creates for hosting are revoked when you disconnect.

Last updated 2026-10-30