<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged dokploy</title>
<link>https://coderlegion.com/tag/dokploy</link>
<description>Powered by Question2Answer</description>
<item>
<title>Mastering Self-Hosted Convex on Dokploy: A Complete Deployment Guide</title>
<link>https://coderlegion.com/24030/mastering-self-hosted-convex-on-dokploy-a-complete-deployment-guide</link>
<description>&lt;p&gt;So, you've decided to take control of your data and self-host &lt;strong&gt;Convex&lt;/strong&gt;. It's a great choice but if you've tried pushing your functions to a local or private server, you might have run into cryptic errors about &quot;Relative import paths&quot; or &quot;Missing modules.&quot;&lt;/p&gt;
&lt;p&gt;The secret? You don't need custom deployment scripts. In fact, you &lt;em&gt;shouldn't&lt;/em&gt; use them. The official Convex CLI is actually powerful enough to handle self-hosted deployments perfectly—if you know which buttons to press.&lt;/p&gt;
&lt;p&gt;In this guide, I’ll show you exactly how to configure your project and deploy your functions like a pro.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-before-you-start&quot;&gt;1. Before You Start&lt;/h3&gt;
&lt;p&gt;Before we dive into the code, make sure you have the following ready:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Your Convex Instance&lt;/strong&gt;: Whether you're using Dokploy, Docker, or a raw VPS, your backend should be up and running.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Admin Key&lt;/strong&gt;: You'll need the master key for your server. &lt;ul&gt;
&lt;li&gt;&lt;em&gt;Tip&lt;/em&gt;: If you're using Dokploy, you can usually generate this by running &lt;code&gt;./generate_admin_key.sh&lt;/code&gt; inside your backend container's terminal.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The CLI&lt;/strong&gt;: Make sure you have the latest version of Convex installed locally:&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install convex
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-preparing-your-project&quot;&gt;2. Preparing Your Project&lt;/h3&gt;
&lt;p&gt;For a smooth deployment, your &lt;code&gt;convex/&lt;/code&gt; folder needs to be a bit more &quot;independent.&quot;&lt;/p&gt;
&lt;h4 id=&quot;step-1-give-convex-its-own-identity&quot;&gt;Step 1: Give Convex its own Identity&lt;/h4&gt;
&lt;p&gt;Create a &lt;code&gt;package.json&lt;/code&gt; inside your &lt;code&gt;convex/&lt;/code&gt; directory. This ensures the CLI knows exactly which dependencies to bundle when it prepares your code for the server.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;name&quot;: &quot;my-convex-backend&quot;,
  &quot;private&quot;: true,
  &quot;type&quot;: &quot;module&quot;,
  &quot;dependencies&quot;: {
    &quot;convex&quot;: &quot;^1.16.0&quot;,
    &quot;typescript&quot;: &quot;^5.0.0&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-2-fix-your-config&quot;&gt;Step 2: Fix your Config&lt;/h4&gt;
&lt;p&gt;Ensure your &lt;code&gt;convex/convex.config.ts&lt;/code&gt; is using the modern &lt;code&gt;defineApp&lt;/code&gt; export. The server expects this structure to understand your app's layout.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { defineApp } from &quot;convex/server&quot;;

export default defineApp();
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-the-magic-environment-variables&quot;&gt;3. The Magic Environment Variables&lt;/h3&gt;
&lt;p&gt;The Convex CLI is &quot;Cloud-first&quot; by default, but we can tell it to target our own server by setting two specific environment variables.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align=&quot;left&quot;&gt;Variable &lt;/th&gt;&lt;th align=&quot;left&quot;&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;CONVEX_SELF_HOSTED_URL&lt;/code&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Points the CLI to your backend URL (e.g., &lt;code&gt;https://api.myapp.com&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;left&quot;&gt;&lt;code&gt;CONVEX_SELF_HOSTED_ADMIN_KEY&lt;/code&gt; &lt;/td&gt;&lt;td align=&quot;left&quot;&gt;Your secret key. &lt;strong&gt;Crucial&lt;/strong&gt;: It must include the &lt;code&gt;convex-self-hosted|&lt;/code&gt; prefix!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-the-big-moment-deploying&quot;&gt;4. The Big Moment: Deploying&lt;/h3&gt;
&lt;p&gt;Now, let's push your code. We're going to use the official &lt;code&gt;deploy&lt;/code&gt; command. &lt;/p&gt;
&lt;p&gt;Run this from your &lt;strong&gt;root&lt;/strong&gt; or &lt;strong&gt;convex&lt;/strong&gt; directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;CONVEX_SELF_HOSTED_URL=&quot;https://your-api-url.com&quot; \
CONVEX_SELF_HOSTED_ADMIN_KEY=&quot;convex-self-hosted|your-key-here&quot; \
npx convex deploy --typecheck disable
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;why-this-works-where-other-scripts-fail-&quot;&gt;Why this works where other scripts fail:&lt;/h4&gt;
&lt;p&gt;When you use a custom &lt;code&gt;fetch&lt;/code&gt; script to send files, you're just sending raw text. The server has no idea where &lt;code&gt;convex/server&lt;/code&gt; is. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The CLI is smarter.&lt;/strong&gt; It uses &lt;code&gt;esbuild&lt;/code&gt; to &quot;bundle&quot; your code into a single, self-contained JavaScript package. It finds all your imports and bakes them right into the file so the server can run them instantly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-pro-tip-automate-everything&quot;&gt;5. Pro Tip: Automate Everything&lt;/h3&gt;
&lt;p&gt;Don't type those long variables every time. Add a script to your &lt;code&gt;convex/package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;scripts&quot;: {
  &quot;deploy&quot;: &quot;CONVEX_SELF_HOSTED_URL=... CONVEX_SELF_HOSTED_ADMIN_KEY=... npx convex deploy --typecheck disable&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, all it takes is a simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm run deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;troubleshooting-common-gotchas-&quot;&gt;Troubleshooting common &quot;Gotchas&quot;&lt;/h3&gt;
&lt;h4 id=&quot;-relative-import-path-convex-server-not-found-&quot;&gt;&quot;Relative import path 'convex/server' not found&quot;&lt;/h4&gt;
&lt;p&gt;If you see this, you likely bypassed the CLI. Always use &lt;code&gt;npx convex deploy&lt;/code&gt; so the bundler can do its job!&lt;/p&gt;
&lt;h4 id=&quot;-badadminkey-&quot;&gt;&quot;BadAdminKey&quot;&lt;/h4&gt;
&lt;p&gt;Double-check your key. Most people forget the &lt;code&gt;convex-self-hosted|&lt;/code&gt; prefix. Without it, the server will reject your request every time.&lt;/p&gt;
&lt;h4 id=&quot;missing-generated-files-in-your-frontend&quot;&gt;Missing &lt;code&gt;_generated&lt;/code&gt; files in your frontend&lt;/h4&gt;
&lt;p&gt;Run &lt;code&gt;npx convex codegen&lt;/code&gt; while your &lt;code&gt;CONVEX_SELF_HOSTED_URL&lt;/code&gt; is set to update your local TypeScript types to match what you just deployed.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Happy hosting! Your Convex backend is now truly yours.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24030/mastering-self-hosted-convex-on-dokploy-a-complete-deployment-guide</guid>
<pubDate>Mon, 03 Aug 2026 07:41:58 +0000</pubDate>
</item>
<item>
<title>The Issues of Deploying Laravel on Dokploy: Database Connection Hell</title>
<link>https://coderlegion.com/24029/the-issues-of-deploying-laravel-on-dokploy-database-connection-hell</link>
<description>&lt;p&gt;If you’ve tried deploying a Laravel application on &lt;strong&gt;Dokploy&lt;/strong&gt; (the self-hosted Heroku alternative built on Docker) and immediately ran into a wall of &lt;code&gt;Database connection refused&lt;/code&gt; or &lt;code&gt;SQLSTATE[HY000] [2002]&lt;/code&gt; errors, you are not alone. &lt;/p&gt;
&lt;p&gt;It works perfectly on your local machine, but the moment it hits the server, it falls apart. Here is a real-world breakdown of the database connectivity issues we experienced, why they happened, and how to fix them.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-the-localhost-trap-the-127-0-0-1-connection-refused-&quot;&gt;1. The &quot;Localhost&quot; Trap (The &lt;code&gt;127.0.0.1&lt;/code&gt; Connection Refused)&lt;/h3&gt;
&lt;h4 id=&quot;the-issue&quot;&gt;The Issue&lt;/h4&gt;
&lt;p&gt;On your local computer, you probably set your &lt;code&gt;.env&lt;/code&gt; to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;DB_HOST=127.0.0.1
DB_PORT=3306
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you deploy this to Dokploy, your Laravel application crashes on start because it cannot connect to the database.&lt;/p&gt;
&lt;h4 id=&quot;the-reason&quot;&gt;The Reason&lt;/h4&gt;
&lt;p&gt;Dokploy runs everything in isolated &lt;strong&gt;Docker Containers&lt;/strong&gt;. &lt;br&gt;
Inside a container, &lt;code&gt;127.0.0.1&lt;/code&gt; or &lt;code&gt;localhost&lt;/code&gt; refers to &lt;strong&gt;the Laravel container itself&lt;/strong&gt;, not the host machine or your database container. Since there is no database running &lt;em&gt;inside&lt;/em&gt; your Laravel code container, it refuses the connection.&lt;/p&gt;
&lt;h4 id=&quot;the-solution&quot;&gt;The Solution&lt;/h4&gt;
&lt;p&gt;You must use Docker's internal networking. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If your database is created inside Dokploy, Dokploy assigns it a specific network service name (e.g. &lt;code&gt;dokploy-db-mysql&lt;/code&gt; or the service name you typed in the database creation wizard).&lt;/li&gt;
&lt;li&gt;Change your host in the Dokploy Environment Variables UI to match that service name:&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;DB_HOST=dokploy-db-mysql  # Use Dokploy's internal container name
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-the-config-caching-nightmare-why-changing-env-variables-in-the-ui-did-nothing-&quot;&gt;2. The Config Caching Nightmare (Why changing Env Variables in the UI did nothing)&lt;/h3&gt;
&lt;h4 id=&quot;the-issue-1&quot;&gt;The Issue&lt;/h4&gt;
&lt;p&gt;You realized the host was wrong, went to the Dokploy dashboard, updated the &lt;code&gt;DB_HOST&lt;/code&gt; environment variable to the correct container hostname, saved, and redeployed. &lt;br&gt;
Yet, the app &lt;em&gt;still&lt;/em&gt; threw the old connection error looking for &lt;code&gt;127.0.0.1&lt;/code&gt;. You checked the container logs, confirmed the new environment variables were set, but Laravel simply ignored them.&lt;/p&gt;
&lt;h4 id=&quot;the-reason-1&quot;&gt;The Reason&lt;/h4&gt;
&lt;p&gt;This happens if you run configuration caching in your &lt;code&gt;Dockerfile&lt;/code&gt; during the build step:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;RUN php artisan config:cache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When Laravel builds your container, it runs &lt;code&gt;config:cache&lt;/code&gt; and &lt;strong&gt;bakes&lt;/strong&gt; whatever &lt;code&gt;.env&lt;/code&gt; values exist at build-time directly into a static cache file (&lt;code&gt;bootstrap/cache/config.php&lt;/code&gt;). &lt;br&gt;
At runtime, Laravel completely ignores your Dokploy UI environment variables and loads the baked-in values instead.&lt;/p&gt;
&lt;h4 id=&quot;the-solution-1&quot;&gt;The Solution&lt;/h4&gt;
&lt;p&gt;Never cache your configuration during the Docker build stage if you plan to change environment variables dynamically.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Remove &lt;code&gt;RUN php artisan config:cache&lt;/code&gt; from your build steps.&lt;/li&gt;
&lt;li&gt;If you want caching for speed in production, clear it on boot by running &lt;code&gt;php artisan config:clear&lt;/code&gt; or &lt;code&gt;php artisan optimize:clear&lt;/code&gt; inside your container's startup command / entrypoint script.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-the-race-condition-migrations-crashing-the-build-&quot;&gt;3. The Race Condition (Migrations crashing the build)&lt;/h3&gt;
&lt;h4 id=&quot;the-issue-2&quot;&gt;The Issue&lt;/h4&gt;
&lt;p&gt;You configured your Docker container to automatically run migrations on deployment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;php artisan migrate --force
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But your deployment failed during this step because the database connection timed out or was refused, even though all credentials were 100% correct.&lt;/p&gt;
&lt;h4 id=&quot;the-reason-2&quot;&gt;The Reason&lt;/h4&gt;
&lt;p&gt;When Dokploy restarts or provisions your stack, it starts both the Laravel container and the MySQL/PostgreSQL container simultaneously.&lt;br&gt;
Laravel boots up in milliseconds and immediately tries to run migrations. However, MySQL is still executing its slow initial setup scripts, checking schemas, and opening sockets. The database container isn't ready to receive queries yet, so Laravel's migration command crashes, failing the entire Dokploy deployment.&lt;/p&gt;
&lt;h4 id=&quot;the-solution-2&quot;&gt;The Solution&lt;/h4&gt;
&lt;p&gt;You need to make Laravel &quot;wait&quot; for the database to boot. Add a small bash loop to your entrypoint script (&lt;code&gt;entrypoint.sh&lt;/code&gt;) that pings the database port before running migrations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo &quot;Waiting for MySQL to boot...&quot;
until nc -z -v -w30 $DB_HOST $DB_PORT; do
  echo &quot;MySQL is not ready yet. Retrying in 2 seconds...&quot;
  sleep 2
done
echo &quot;MySQL is up! Running migrations...&quot;
php artisan migrate --force
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-port-mapping-confusion-internal-vs-external-ports-&quot;&gt;4. Port Mapping Confusion (Internal vs. External Ports)&lt;/h3&gt;
&lt;h4 id=&quot;the-issue-3&quot;&gt;The Issue&lt;/h4&gt;
&lt;p&gt;Mapping your Dokploy MySQL database port to &lt;code&gt;3306&lt;/code&gt; (or &lt;code&gt;3307&lt;/code&gt;) externally so you could connect via TablePlus/DBeaver from your laptop, but when configuring your Laravel &lt;code&gt;.env&lt;/code&gt; inside Dokploy, you weren't sure which port to use.&lt;/p&gt;
&lt;h4 id=&quot;the-reason-3&quot;&gt;The Reason&lt;/h4&gt;
&lt;p&gt;Docker has two layers of ports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Internal Port:&lt;/strong&gt; The port the service listens to inside the Docker private network (always &lt;code&gt;3306&lt;/code&gt; for MySQL).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External/Exposed Port:&lt;/strong&gt; The port mapped to the public server IP so you can connect from the outside world (e.g., mapping host &lt;code&gt;3307&lt;/code&gt; to container &lt;code&gt;3306&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inside Dokploy, your Laravel app and your MySQL container share the same private network. Therefore, they communicate &lt;strong&gt;internally&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;the-solution-3&quot;&gt;The Solution&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;Laravel (.env on Dokploy)&lt;/strong&gt;: Always use the internal port (&lt;code&gt;DB_PORT=3306&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;TablePlus/DBeaver (External laptop)&lt;/strong&gt;: Use the exposed host port (&lt;code&gt;3307&lt;/code&gt;) and the public server IP.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;summary-checklist-for-dokploy-deployments&quot;&gt;Summary Checklist for Dokploy deployments&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;DB_HOST&lt;/code&gt; to the internal Docker container/service name, not &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;DB_PORT&lt;/code&gt; to the internal port (&lt;code&gt;3306&lt;/code&gt; for MySQL), not the exposed host port.&lt;/li&gt;
&lt;li&gt;Avoid running &lt;code&gt;php artisan config:cache&lt;/code&gt; during Docker build time.&lt;/li&gt;
&lt;li&gt;Implement a &lt;code&gt;wait-for-it&lt;/code&gt; check in your startup script so migrations don't run before MySQL is fully booted.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24029/the-issues-of-deploying-laravel-on-dokploy-database-connection-hell</guid>
<pubDate>Mon, 03 Aug 2026 07:41:12 +0000</pubDate>
</item>
</channel>
</rss>