<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>coolify &amp;mdash; Blog - Kais BETTAIEB</title>
    <link>https://blog.kaisbettaieb.dev/tag:coolify</link>
    <description>Python Developer crafting robust applications at the intersection of design, technology, and user experience.</description>
    <pubDate>Tue, 28 Apr 2026 11:08:55 +0000</pubDate>
    <item>
      <title>How to Deploy Next.js on Coolify v4 Using a Custom Dockerfile</title>
      <link>https://blog.kaisbettaieb.dev/how-to-deploy-next-js-on-coolify-v4-using-a-custom-dockerfile</link>
      <description>&lt;![CDATA[#deploy #nextjs #coolify #Docker #github #selfhost&#xA;&#xA;Coolify has emerged as a premier self-hosted alternative to Vercel and Netlify. While its default Nixpacks builder is excellent for zero-configuration deployments, developers often require granular control over the build environment.&#xA;&#xA;This guide demonstrates how to deploy a Next.js application on Coolify v4 (specifically tested on v4.0.0-beta.460) using a multi-stage Dockerfile. This method ensures a highly optimized, production-ready image.&#xA;!--more--&#xA;&#xA;Prerequisites&#xA;&#xA;A running instance of Coolify v4 (Self-hosted).&#xA;A Next.js project pushed to a Git repository (GitHub, GitLab, etc.).&#xA;&#xA;Step 1: Optimize Next.js for Docker&#xA;&#xA;The most critical step before configuring the container is setting up Next.js to create a standalone build. This feature automatically traces your import dependencies and creates a smaller deployment folder, eliminating the need to copy the entire nodemodules directory into your final image.&#xA;&#xA;Open your next.config.js file and add the output: &#39;standalone&#39; configuration:&#xA;&#xA;next.config.js&#xA;/* @type {import(&#39;next&#39;).NextConfig} /&#xA;const nextConfig = {&#xA;  output: &#34;standalone&#34;,&#xA;};&#xA;&#xA;module.exports = nextConfig;&#xA;&#xA;Step 2: Create the Multi-Stage Dockerfile&#xA;&#xA;Create a file named Dockerfile in the root of your project. We will use a multi-stage build strategy to keep the final image lightweight. This process splits the build into three stages:&#xA;Deps: Installs dependencies (cached for speed).&#xA;Builder: Compiles the Next.js application.&#xA;Runner: The final, minimal production image.&#xA;&#xA;Copy the following code into your Dockerfile:&#xA;&#xA;Dockerfile&#xA;Stage 1: Install dependencies&#xA;FROM node:18-alpine AS deps&#xA;WORKDIR /app&#xA;Install libc6-compat (needed for some native dependencies)&#xA;RUN apk add --no-cache libc6-compat&#xA;COPY package.json package-lock.json ./&#xA;RUN npm ci&#xA;&#xA;Stage 2: Build the application&#xA;FROM node:18-alpine AS builder&#xA;WORKDIR /app&#xA;COPY --from=deps /app/nodemodules ./nodemodules&#xA;COPY . .&#xA;Disable Next.js telemetry during build&#xA;ENV NEXTTELEMETRYDISABLED 1&#xA;RUN npm run build&#xA;&#xA;Stage 3: Production runner&#xA;FROM node:18-alpine AS runner&#xA;WORKDIR /app&#xA;ENV NODEENV production&#xA;ENV NEXTTELEMETRYDISABLED 1&#xA;&#xA;Create a system user for security&#xA;RUN addgroup --system --gid 1001 nodejs&#xA;RUN adduser --system --uid 1001 nextjs&#xA;&#xA;Copy the standalone build and static assets&#xA;COPY --from=builder /app/public ./public&#xA;COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./&#xA;COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static&#xA;&#xA;USER nextjs&#xA;EXPOSE 3000&#xA;ENV PORT 3000&#xA;CMD [&#34;node&#34;, &#34;server.js&#34;]&#xA;&#xA;  Note: Create a .dockerignore file in your root directory and add nodemodules and .next to it. This prevents local build files from slowing down the Docker build context.&#xA;&#xA;Step 3: Configure the Project in Coolify&#xA;&#xA;Log in to your Coolify dashboard.&#xA;Click + New Resource and select Public Repository (or Private Repository if you have configured GitHub App access).&#xA;Paste your repository URL and select the branch you wish to deploy (usually main).&#xA;Under Build Pack, select Dockerfile.&#xA;    Coolify defaults to Nixpacks, so this manual selection is crucial.*&#xA;Click Continue.&#xA;&#xA;Step 4: Define Environment Variables&#xA;&#xA;If your Next.js application relies on environment variables (like DATABASEURL or APIKEY), you must define them in Coolify before deploying.&#xA;&#xA;Navigate to the Environment Variables tab in your project view.&#xA;Add your keys and values.&#xA;Ensure you do not commit a .env file to your Git repository; managing them inside Coolify is more secure.&#xA;&#xA;Step 5: Deploy&#xA;&#xA;Go back to the Configuration tab.&#xA;Verify the Ports Exposes setting is set to 3000 (matching the EXPOSE instruction in our Dockerfile).&#xA;Click Deploy in the top right corner.&#xA;&#xA;Coolify will now pull your code, build the Docker image using the stages defined in Step 2, and start the container. You can view the build logs in real-time to monitor the progress. Once the status changes to Running, your optimized Next.js application is live.&#xA;&#xA;div class=&#34;blog-signature&#34;&#xD;&#xA;    div class=&#34;sig-content&#34;&#xD;&#xA;        pThanks for reading! If you found this helpful:/p&#xD;&#xA;        div class=&#34;sig-links&#34;&#xD;&#xA;            a href=&#34;https://ko-fi.com/kaisbettaieb&#34; class=&#34;kofi-btn&#34; target=&#34;blank&#34; rel=&#34;noopener&#34;☕ Buy me a coffee/a&#xD;&#xA;            span class=&#34;divider&#34;or/span&#xD;&#xA;            a href=&#34;https://github.com/kaisbettaieb&#34; class=&#34;text-link&#34;Check my Code/a&#xD;&#xA;            span class=&#34;divider&#34;or/span&#xD;&#xA;            a href=&#34;https://kbt.dev&#34; class=&#34;text-link&#34;More about me/a&#xD;&#xA;        /div&#xD;&#xA;    /div&#xD;&#xA;/div&#xD;&#xA;div id=&#34;comment-section&#34;/div&#xD;&#xA;&#xD;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://blog.kaisbettaieb.dev/tag:deploy" class="hashtag"><span>#</span><span class="p-category">deploy</span></a> <a href="https://blog.kaisbettaieb.dev/tag:nextjs" class="hashtag"><span>#</span><span class="p-category">nextjs</span></a> <a href="https://blog.kaisbettaieb.dev/tag:coolify" class="hashtag"><span>#</span><span class="p-category">coolify</span></a> <a href="https://blog.kaisbettaieb.dev/tag:Docker" class="hashtag"><span>#</span><span class="p-category">Docker</span></a> <a href="https://blog.kaisbettaieb.dev/tag:github" class="hashtag"><span>#</span><span class="p-category">github</span></a> <a href="https://blog.kaisbettaieb.dev/tag:selfhost" class="hashtag"><span>#</span><span class="p-category">selfhost</span></a></p>

<p>Coolify has emerged as a premier self-hosted alternative to Vercel and Netlify. While its default Nixpacks builder is excellent for zero-configuration deployments, developers often require granular control over the build environment.</p>

<p>This guide demonstrates how to deploy a Next.js application on <strong>Coolify v4</strong> (specifically tested on v4.0.0-beta.460) using a multi-stage Dockerfile. This method ensures a highly optimized, production-ready image.
</p>

<h3 id="prerequisites">Prerequisites</h3>
<ul><li>A running instance of <strong>Coolify v4</strong> (Self-hosted).</li>
<li>A Next.js project pushed to a Git repository (GitHub, GitLab, etc.).</li></ul>

<h3 id="step-1-optimize-next-js-for-docker">Step 1: Optimize Next.js for Docker</h3>

<p>The most critical step before configuring the container is setting up Next.js to create a standalone build. This feature automatically traces your import dependencies and creates a smaller deployment folder, eliminating the need to copy the entire <code>node_modules</code> directory into your final image.</p>

<p>Open your <code>next.config.js</code> file and add the <code>output: &#39;standalone&#39;</code> configuration:</p>

<p><strong>next.config.js</strong></p>

<pre><code class="language-javascript">/** @type {import(&#39;next&#39;).NextConfig} */
const nextConfig = {
  output: &#34;standalone&#34;,
};

module.exports = nextConfig;
</code></pre>

<h3 id="step-2-create-the-multi-stage-dockerfile">Step 2: Create the Multi-Stage Dockerfile</h3>

<p>Create a file named <code>Dockerfile</code> in the root of your project. We will use a multi-stage build strategy to keep the final image lightweight. This process splits the build into three stages:
1.  <strong>Deps:</strong> Installs dependencies (cached for speed).
2.  <strong>Builder:</strong> Compiles the Next.js application.
3.  <strong>Runner:</strong> The final, minimal production image.</p>

<p>Copy the following code into your <code>Dockerfile</code>:</p>

<p><strong>Dockerfile</strong></p>

<pre><code class="language-dockerfile"># Stage 1: Install dependencies
FROM node:18-alpine AS deps
WORKDIR /app
# Install libc6-compat (needed for some native dependencies)
RUN apk add --no-cache libc6-compat
COPY package.json package-lock.json* ./
RUN npm ci

# Stage 2: Build the application
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Disable Next.js telemetry during build
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build

# Stage 3: Production runner
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

# Create a system user for security
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy the standalone build and static assets
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD [&#34;node&#34;, &#34;server.js&#34;]
</code></pre>

<blockquote><p><strong>Note:</strong> Create a <code>.dockerignore</code> file in your root directory and add <code>node_modules</code> and <code>.next</code> to it. This prevents local build files from slowing down the Docker build context.</p></blockquote>

<h3 id="step-3-configure-the-project-in-coolify">Step 3: Configure the Project in Coolify</h3>
<ol><li>Log in to your Coolify dashboard.</li>
<li>Click <strong>+ New Resource</strong> and select <strong>Public Repository</strong> (or <strong>Private Repository</strong> if you have configured GitHub App access).</li>
<li>Paste your repository URL and select the branch you wish to deploy (usually <code>main</code>).</li>
<li>Under <strong>Build Pack</strong>, select <strong>Dockerfile</strong>.
<ul><li><em>Coolify defaults to Nixpacks, so this manual selection is crucial.</em></li></ul></li>
<li>Click <strong>Continue</strong>.</li></ol>

<h3 id="step-4-define-environment-variables">Step 4: Define Environment Variables</h3>

<p>If your Next.js application relies on environment variables (like <code>DATABASE_URL</code> or <code>API_KEY</code>), you must define them in Coolify before deploying.</p>
<ol><li>Navigate to the <strong>Environment Variables</strong> tab in your project view.</li>
<li>Add your keys and values.</li>
<li>Ensure you do <strong>not</strong> commit a <code>.env</code> file to your Git repository; managing them inside Coolify is more secure.</li></ol>

<h3 id="step-5-deploy">Step 5: Deploy</h3>
<ol><li>Go back to the <strong>Configuration</strong> tab.</li>
<li>Verify the <strong>Ports Exposes</strong> setting is set to <code>3000</code> (matching the <code>EXPOSE</code> instruction in our Dockerfile).</li>
<li>Click <strong>Deploy</strong> in the top right corner.</li></ol>

<p>Coolify will now pull your code, build the Docker image using the stages defined in Step 2, and start the container. You can view the build logs in real-time to monitor the progress. Once the status changes to <strong>Running</strong>, your optimized Next.js application is live.</p>

<div class="blog-signature">
    <div class="sig-content">
        <p>Thanks for reading! If you found this helpful:</p>
        <div class="sig-links">
            <a href="https://ko-fi.com/kaisbettaieb" class="kofi-btn" target="_blank">☕ Buy me a coffee</a>
            <span class="divider">or</span>
            <a href="https://github.com/kaisbettaieb" class="text-link">Check my Code</a>
            <span class="divider">or</span>
            <a href="https://kbt.dev" class="text-link">More about me</a>
        </div>
    </div>
</div>
<div id="comment-section"></div>
]]></content:encoded>
      <guid>https://blog.kaisbettaieb.dev/how-to-deploy-next-js-on-coolify-v4-using-a-custom-dockerfile</guid>
      <pubDate>Fri, 16 Jan 2026 00:51:24 +0000</pubDate>
    </item>
    <item>
      <title>Deploying WriteFreely on a Coolify VPS with MySQL</title>
      <link>https://blog.kaisbettaieb.dev/deploying-writefreely-on-a-coolify-vps-with-mysql</link>
      <description>&lt;![CDATA[#coolify #writefreely #vps #mysql&#xA;&#xA;I currently run a Coolify instance on my VPS and recently decided to host a lightweight blogging platform. I managed to deploy WriteFreely using the open-source algernon/writefreely Docker image. While the initial deployment was straightforward, configuring the storage and database correctly required some specific steps. Here is a walkthrough of how I achieved a stable setup.&#xA;!--more--&#xA;&#xA;1. The Docker Configuration&#xA;&#xA;First, I pulled the Docker image and deployed it via Coolify. The primary challenge was configuring persistent storage. By default, if you restart the container, you lose your configuration, which makes the service unusable in production.&#xA;&#xA;To address this, I configured Coolify to create a persistent volume. I mapped a volume named wfdata to /data inside the container. This step is critical because the config.ini file needs to reside in this directory to persist across restarts.&#xA;&#xA;2. Adding a Standalone MySQL Database&#xA;&#xA;I preferred not to use the default SQLite engine, so I decided to provision a standalone MySQL database service within Coolify. The process is remarkably straightforward: you simply create a new resource, select MySQL, and let Coolify handle the password generation.&#xA;&#xA;The tricky part was establishing the connection between the app and the database. I initially encountered &#34;Access Denied&#34; errors because I was attempting to use the default mysql user. Once I updated the credentials in the configuration file to use the root user (or a dedicated user with proper permissions), the connection worked perfectly.&#xA;&#xA;3. Linking the Components&#xA;&#xA;To ensure WriteFreely correctly reads from the persistent volume, I had to modify the startup command. I couldn&#39;t immediately locate the command field in the UI, so I utilized the Docker Compose configuration to override the entry point:&#xA;&#xA;/bin/sh -c &#34;./writefreely -c /data/config.ini&#34;&#xA;&#xA;Additionally, I ran the configuration wizard directly inside the terminal to generate the initial config.ini. I made sure to point the encryption keys and database paths specifically to /data.&#xA;&#xA;Conclusion&#xA;&#xA;I now have a fully functional WriteFreely instance running on my VPS, complete with persistent storage and a robust MySQL backend. It is a highly efficient and self-contained setup.&#xA;&#xA;div class=&#34;blog-signature&#34;&#xD;&#xA;    div class=&#34;sig-content&#34;&#xD;&#xA;        pThanks for reading! If you found this helpful:/p&#xD;&#xA;        div class=&#34;sig-links&#34;&#xD;&#xA;            a href=&#34;https://ko-fi.com/kaisbettaieb&#34; class=&#34;kofi-btn&#34; target=&#34;blank&#34; rel=&#34;noopener&#34;☕ Buy me a coffee/a&#xD;&#xA;            span class=&#34;divider&#34;or/span&#xD;&#xA;            a href=&#34;https://github.com/kaisbettaieb&#34; class=&#34;text-link&#34;Check my Code/a&#xD;&#xA;            span class=&#34;divider&#34;or/span&#xD;&#xA;            a href=&#34;https://kbt.dev&#34; class=&#34;text-link&#34;More about me/a&#xD;&#xA;        /div&#xD;&#xA;    /div&#xD;&#xA;/div&#xD;&#xA;div id=&#34;comment-section&#34;/div&#xD;&#xA;&#xD;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://blog.kaisbettaieb.dev/tag:coolify" class="hashtag"><span>#</span><span class="p-category">coolify</span></a> <a href="https://blog.kaisbettaieb.dev/tag:writefreely" class="hashtag"><span>#</span><span class="p-category">writefreely</span></a> <a href="https://blog.kaisbettaieb.dev/tag:vps" class="hashtag"><span>#</span><span class="p-category">vps</span></a> <a href="https://blog.kaisbettaieb.dev/tag:mysql" class="hashtag"><span>#</span><span class="p-category">mysql</span></a></p>

<p>I currently run a <strong>Coolify</strong> instance on my VPS and recently decided to host a lightweight blogging platform. I managed to deploy <strong>WriteFreely</strong> using the open-source <code>algernon/writefreely</code> Docker image. While the initial deployment was straightforward, configuring the storage and database correctly required some specific steps. Here is a walkthrough of how I achieved a stable setup.
</p>

<h2 id="1-the-docker-configuration">1. The Docker Configuration</h2>

<p>First, I pulled the Docker image and deployed it via Coolify. The primary challenge was configuring <strong>persistent storage</strong>. By default, if you restart the container, you lose your configuration, which makes the service unusable in production.</p>

<p>To address this, I configured Coolify to create a persistent volume. I mapped a volume named <code>wf_data</code> to <code>/data</code> inside the container. This step is critical because the <code>config.ini</code> file needs to reside in this directory to persist across restarts.</p>

<h2 id="2-adding-a-standalone-mysql-database">2. Adding a Standalone MySQL Database</h2>

<p>I preferred not to use the default SQLite engine, so I decided to <strong>provision a standalone MySQL database service</strong> within Coolify. The process is remarkably straightforward: you simply create a new resource, select MySQL, and let Coolify handle the password generation.</p>

<p>The tricky part was establishing the connection between the app and the database. I initially encountered “Access Denied” errors because I was attempting to use the default <code>mysql</code> user. Once I updated the credentials in the configuration file to use the <code>root</code> user (or a dedicated user with proper permissions), the connection worked perfectly.</p>

<h2 id="3-linking-the-components">3. Linking the Components</h2>

<p>To ensure WriteFreely correctly reads from the persistent volume, I had to modify the startup command. I couldn&#39;t immediately locate the command field in the UI, so I utilized the <strong>Docker Compose</strong> configuration to override the entry point:</p>

<pre><code class="language-bash">/bin/sh -c &#34;./writefreely -c /data/config.ini&#34;
</code></pre>

<p>Additionally, I ran the configuration wizard directly inside the terminal to generate the initial <code>config.ini</code>. I made sure to point the encryption keys and database paths specifically to <code>/data</code>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I now have a fully functional WriteFreely instance running on my VPS, complete with persistent storage and a robust MySQL backend. It is a highly efficient and self-contained setup.</p>

<div class="blog-signature">
    <div class="sig-content">
        <p>Thanks for reading! If you found this helpful:</p>
        <div class="sig-links">
            <a href="https://ko-fi.com/kaisbettaieb" class="kofi-btn" target="_blank">☕ Buy me a coffee</a>
            <span class="divider">or</span>
            <a href="https://github.com/kaisbettaieb" class="text-link">Check my Code</a>
            <span class="divider">or</span>
            <a href="https://kbt.dev" class="text-link">More about me</a>
        </div>
    </div>
</div>
<div id="comment-section"></div>
]]></content:encoded>
      <guid>https://blog.kaisbettaieb.dev/deploying-writefreely-on-a-coolify-vps-with-mysql</guid>
      <pubDate>Fri, 09 Jan 2026 09:27:18 +0000</pubDate>
    </item>
  </channel>
</rss>