Deploying a Next.js App on Coolify: What Self-Hosting My Site Taught Me / Oct 24, 2026

7 min readBy Emmanuel Akinfulubi
Deploying a Next.js App on Coolify: What Self-Hosting My Site Taught Me

This site, emmanuelakbi.com, runs on Coolify on one VPS, and so does a bilingual charity site I delivered this year. Coolify is an open source platform you install on your own server. It gives you much of what Vercel or Netlify give you: deploy on git push, a dashboard, free TLS certificates, environment variables and rollbacks. The difference is that the server is yours, and so is the work of looking after it.

This is not a "Coolify beats everything" post. It is what I set up, the details that cost me time, and how to decide whether it is right for your project.

What you are signing up for

On a managed host, someone else patches the server, watches the disk and keeps the edge network fast. On Coolify, that is you. In return you get:

  • One bill for the server, not a bill per project or per seat.
  • Your data on a machine you control, which matters for some clients.
  • Any language or framework, because everything runs as a Docker container.

You give up:

  • A global edge network. Your site is served from wherever the VPS is. You can put a CDN in front of it.
  • Zero ops. Updates, backups and disk space are your job.
  • Automatic scaling. One server is one server.

For a portfolio, a client marketing site or a small product, that trade is usually fine. For an app with heavy traffic spikes, a managed host is still the easier answer.

Install Coolify

Coolify's docs ask for at least 2 CPU cores, 2 GB of RAM and 10 GB of disk. Start with a fresh Ubuntu LTS server, then run the official installer:

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | sudo bash

It installs Docker and Coolify, then serves the dashboard on port 8000. Create the admin account straight away, before anyone else finds the page, and then put the dashboard behind its own domain.

Connect the repository the right way

If your repository is private, Coolify needs permission to clone it. There are two ways, and only one of them deploys on push:

  • GitHub App. Install Coolify's GitHub App on your account and grant it the repo. It also wires up the webhooks, so every push to your branch triggers a deploy.
  • Deploy key. Coolify gives you an SSH key to add to the repo. It is simpler, but you get no automatic deploys. You redeploy from the dashboard or call Coolify's webhook yourself.

I use the GitHub App. One more small trap: set the branch to the one you actually use. This repo's default branch is master, not main.

Use a Dockerfile, and get three details right

Coolify can build with Nixpacks, which detects your framework and needs no setup. I use a Dockerfile instead, because Next.js has a standalone output mode that produces a much smaller image, and that mode has requirements Nixpacks does not know about.

Turn it on in next.config.mjs:

const nextConfig = {
  output: "standalone",
};

export default nextConfig;

Then the runtime stage of the Dockerfile. These are the lines that matter:

FROM node:24-slim AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
# 1. Listen on every interface, or Coolify's proxy cannot reach the container.
ENV HOSTNAME=0.0.0.0

# 2. The standalone server does not include these. Copy them next to it.
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

# 3. Anything read from disk at runtime must be copied too.
COPY --from=builder /app/content ./content

# node:slim has no curl, so check health with Node's built-in fetch.
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
  CMD node -e "fetch('http://127.0.0.1:3000/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"

USER node
EXPOSE 3000
CMD ["node", "server.js"]

Each numbered line cost me time at some point:

  1. HOSTNAME=0.0.0.0. Without it, the standalone server only listens inside the container. The deploy looks healthy and the site never loads.
  2. public/ and .next/static. The standalone bundle leaves them out on purpose. Forget them and the page loads with no CSS, no images and no client JavaScript.
  3. Files your code reads at runtime. My blog posts are MDX files read with fs. Next's file tracer cannot see a path built at runtime, so they were missing from the image. The worst part is that it failed silently: one route returned an empty list instead of an error. I now copy content/ explicitly, and also list it in outputFileTracingIncludes.

In Coolify, set the build pack to Dockerfile and the port to 3000, matching EXPOSE.

The environment variable trap

This one is worth reading twice. Saving an environment variable in Coolify does not change the running app. The status still says Running, the old value stays in effect, and nothing tells you why. Click Deploy and let it finish.

There is a second layer for Next.js. Any variable starting with NEXT_PUBLIC_ is written into the browser bundle during next build. Setting it only at runtime does nothing. In Coolify, mark it as a build variable, and declare it in the Dockerfile's build stage:

ARG NEXT_PUBLIC_GA_ID
ENV NEXT_PUBLIC_GA_ID=$NEXT_PUBLIC_GA_ID
RUN npm run build

Server-only secrets, like an email API key read inside an API route, are fine as runtime variables.

Domain and TLS

Coolify runs Traefik as its proxy and gets Let's Encrypt certificates for you. Three things make that work:

  • Put the scheme in the domain field: https://example.com, not example.com. The https:// is what tells Coolify to request a certificate.
  • Open ports 80 and 443 on the server firewall. Let's Encrypt checks over port 80, so a closed port means no certificate and a browser warning with no obvious cause.
  • Clean up the registrar's DNS. My domain is at Namecheap, and a new domain comes with parking records. Delete them, add A records for @ and www pointing at the server, and confirm with dig rather than trusting the dashboard:
dig +short example.com
dig +short www.example.com

Both should print your server's IP. Then redirect www to the bare domain, or the other way round, so search engines see one site and not two.

Size the server for the build, not the site

A running Next.js site uses little memory. next build uses a lot. Coolify builds on the same server by default, so the build is what decides your server size. 2 GB is the floor and 4 GB is comfortable. If builds start dying, add swap, or build the image in CI and point Coolify at the finished image.

Rollbacks and the boring parts

Coolify keeps previous deployments. If a release breaks something, open the Deployments tab and redeploy the last good one. It takes about as long as a normal deploy, and it is worth trying once before you need it.

Beyond that, put a reminder in your calendar to apply server updates and check disk space. Old Docker images pile up over time. Coolify can clean them up for you, and it is safer than running prune commands by hand. A careless docker system prune --volumes can delete a database's data along with the junk.

One thing not to put on the same server

Do not run your own mail server next to your sites. Many VPS providers block outbound port 25 by default, a new IP has no sending reputation, and some mail stacks want ports 80 and 443, which Traefik already uses. I keep the mailbox with a mail provider and send app email through a transactional service. If mail breaks, the cost is a client enquiry you never see.

Is it worth it?

For me, yes. My sites cost one server bill, deploy on push, and I know exactly where everything lives. But the honest test is this: if you would not notice a full disk or a missed security update for a month, use a managed host. If you are happy to spend a few minutes a week on the server, Coolify gives you a lot for very little.

Further reading: Coolify's installation docs and the Next.js guide to self-hosting.

Frequently asked questions

Is Coolify free?
The self-hosted version is open source and free to run. You pay for the server it runs on. Coolify also sells a hosted dashboard, Coolify Cloud, if you would rather not run the control panel yourself.
How much server do I need for Coolify and a Next.js app?
Coolify's own docs ask for at least 2 CPU cores, 2 GB of RAM and 10 GB of disk. The Next.js build is the hungry part, so 4 GB of RAM is more comfortable. If builds get killed for running out of memory, add swap or build the image in CI and let Coolify pull it.
Why does my Coolify app ignore a new environment variable?
Saving a variable in Coolify does not change the container that is already running. You have to redeploy. Variables starting with NEXT_PUBLIC_ also have to be available at build time, because Next.js writes them into the client bundle during the build.
Should I use Nixpacks or a Dockerfile on Coolify?
Nixpacks works for many apps with no setup. A Dockerfile gives you control over the image, which matters for Next.js standalone output. This site uses a Dockerfile.