mumzworld-coolify-app
production

Deploying a GitHub repo on Coolify

A start-to-finish walkthrough: connecting GitHub, creating the application, attaching a domain with HTTPS, and getting the first deployment out. Written against a self-hosted Coolify v4 instance deploying this Next.js app.

Before you start

What has to be in place

  • A Coolify instance with at least one server added and showing Validated under Servers.
  • Ports 80 and 443 open to the internet on that server. Let's Encrypt validates over port 80 — if it is firewalled, certificates never issue.
  • Admin rights on the GitHub organisation, so you can install a GitHub App on it.
  • A repository whose start script boots a server listening on 0.0.0.0 and the port given by process.env.PORT.

Part 1

Connect GitHub to Coolify

  1. Create a GitHub App source

    In Coolify, go to Sources → + Add → GitHub App. Give it a name, pick the organisation that owns your repos, and press Register Now. Coolify hands you off to GitHub to create the App, then back again to install it.

    When GitHub asks which repositories to grant access to, prefer Only select repositories and pick the ones you will actually deploy.

Part 2

Create and configure the application

  1. Add the resource

    Open (or create) a Project, choose an environment, then + New Resource → Private Repository (with GitHub App). Select the source you just made, pick the repository, and choose the branch to deploy.

  2. Set the build configuration

    Coolify detects Next.js and preselects Nixpacks. Leave the three command fields empty — Nixpacks derives them from package.json, and typing them in by hand only creates something else to keep in sync.

    Build PackNixpacksAuto-detected for Next.js. No Dockerfile needed.
    BranchmasterMust match the branch you actually push. This is also what autodeploy watches.
    Base Directory/Change only for a monorepo, e.g. /apps/web.
    Install Command(leave blank)Nixpacks runs npm ci from package-lock.json.
    Build Command(leave blank)Nixpacks runs npm run build.
    Start Command(leave blank)Nixpacks runs npm run start, i.e. next start.
    Ports Exposes3000The port inside the container. Traefik maps 443 to it.
  3. Add environment variables

    Under Environment Variables, paste your keys. The one distinction that matters: anything the browser needs is compiled into the JavaScript bundle at build time, so it has to be present during the build.

    Tick “Build Variable” on these — they are compiled in
    NEXT_PUBLIC_APP_NAME=Coolify Deployment Demo
    NEXT_PUBLIC_ENVIRONMENT=staging
    NEXT_PUBLIC_API_URL=https://api.example.com
    Runtime only — a restart is enough to pick these up
    APP_VERSION=1.0.0
    COMMIT_SHA=$SOURCE_COMMIT
    SERVER_NAME=coolify-prod-01
    NIXPACKS_NODE_VERSION=22

    $SOURCE_COMMIT is a Coolify built-in that resolves to the deployed commit, which is what makes the running version identifiable later.

  4. Configure the health check

    Point Health Checks at an endpoint that actually exercises the process — this app exposes /health.

    Enabledon
    Path/health
    Port3000
    Return Code200
    Interval10s
    Timeout5s
    Retries3
    Start Period20sGrace window while Node boots, before failures start counting.

    Without this, Coolify treats a container as healthy the moment it starts, so a process that boots and then fails to serve can replace a working deployment.

Part 3

Point a domain at it

  1. Create the DNS record first

    Add an A record for the hostname pointing at your server's public IP — or a wildcard, if you want every future app to get a subdomain for free.

    DNS
    A    mumz-app.coolify.mumzstage.com    →  203.0.113.10
    A    *.coolify.mumzstage.com           →  203.0.113.10   (wildcard alternative)
  2. Set the FQDN in Coolify

    In Configuration → General → Domains, enter the full URL including the scheme. The https:// prefix is what tells Traefik to request a certificate; without it you get plain HTTP.

    Domains
    https://mumz-app.coolify.mumzstage.com

    Save, then enable the HTTPS redirect under Advanced so visitors on http:// are bounced up to TLS. Multiple domains can be comma-separated.

Part 4

Deploy for the first time

  1. Press Deploy and watch the logs

    Hit Deploy and open the running deployment to follow the build. Nixpacks moves through four phases — setup, install, build, start — and the log names each one as it goes.

  2. Verify it is really your build

    Do not stop at “the page loads”. Check that the container is running the commit you think it is:

    Terminal
    curl -s https://mumz-app.coolify.mumzstage.com/health | jq

    Confirm commit matches the commit you pushed, and that uptimeSeconds resets to near zero after a restart. If uptime keeps climbing across a redeploy, you are still talking to the old container.

Part 5

Autodeploy on every push

With the GitHub App source and Advanced → Automatic Deployment enabled (the default), a push to the configured branch triggers a fresh build. Three things quietly break it:

  • Branch mismatch. Pushing master while Coolify watches maindeploys nothing, silently.
  • Watch Paths. If set, only commits touching those paths deploy. Leave it empty unless you are deploying one app out of a monorepo.
  • Wrong source type. An app created as a Public Repository has no webhook. Check Webhooks in the sidebar — it shows the URL Coolify expects to be called.

Reference

When it goes wrong

SymptomCause
502 Bad GatewayApp bound to 127.0.0.1 instead of 0.0.0.0, or Ports Exposes does not match the port the server listens on.
Page loads with no stylingNixpacks booted the standalone bundle, which has no copy of .next/static. Remove output: standalone.
NEXT_PUBLIC_* change had no effectNot marked Build Variable, or you restarted instead of redeploying.
Certificate stuck on self-signedDNS not resolving yet, port 80 firewalled, or the FQDN was entered without the https:// scheme.
Wrong Node version at runtime.nvmrc ignored. Pin with NIXPACKS_NODE_VERSION.
Build metadata identical across deploysA page lost export const dynamic = "force-dynamic" and is being prerendered at build time.

Live

What this container is actually running

Read from the environment of the container serving this request, on every request. Italic not set means the variable is absent in Coolify — which is exactly what step 4 fixes.

Baked in at build time

NEXT_PUBLIC_* values, compiled into the bundle. These require a redeploy to change, and are visible to anyone with DevTools.

NEXT_PUBLIC_APP_NAME
mumzworld-coolify-app
NEXT_PUBLIC_ENVIRONMENT
production
NEXT_PUBLIC_API_URL
not set

Read at runtime

From process.env on each request, never sent to the browser. A restart is enough to change these.

APP_VERSION
not set
COMMIT_SHA
not set
DEPLOY_TIME
not set
SERVER_NAME
not set
NODE_ENV
production
PORT
3000
HOSTNAME
49928c8b9c75
process.version
v24.10.0