Skip to content

Commit 24c022f

Browse files
authored
Merge pull request #3647 from Dokploy/2659-upgrading-dokploy-admin-resulted-in-bad-gateway-500-consistently
feat(dokploy): add wait-for-postgres script and update Dockerfile and…
2 parents 08ba24c + ecd81eb commit 24c022f

File tree

5 files changed

+105
-10
lines changed

5 files changed

+105
-10
lines changed

Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,8 @@ RUN curl -sSL https://railpack.com/install.sh | bash
6565
COPY --from=buildpacksio/pack:0.39.1 /usr/local/bin/pack /usr/local/bin/pack
6666

6767
EXPOSE 3000
68-
CMD [ "pnpm", "start" ]
68+
69+
HEALTHCHECK --interval=10s --timeout=3s --retries=10 \
70+
CMD curl -fs http://localhost:3000/api/trpc/settings.health || exit 1
71+
72+
CMD ["sh", "-c", "pnpm run wait-for-postgres && exec pnpm start"]

apps/dokploy/esbuild.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ try {
2525
entryPoints: {
2626
server: "server/server.ts",
2727
migration: "migration.ts",
28+
"wait-for-postgres": "wait-for-postgres.ts",
2829
"reset-password": "reset-password.ts",
2930
"reset-2fa": "reset-2fa.ts",
3031
},

apps/dokploy/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"build-server": "tsx esbuild.config.ts",
1111
"build-next": "next build --webpack",
1212
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
13+
"wait-for-postgres": "node -r dotenv/config dist/wait-for-postgres.mjs",
14+
"wait-for-postgres-dev": "tsx -r dotenv/config wait-for-postgres.ts",
1315
"reset-password": "node -r dotenv/config dist/reset-password.mjs",
1416
"reset-2fa": "node -r dotenv/config dist/reset-2fa.mjs",
1517
"dev": "tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ",

apps/dokploy/server/api/routers/settings.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -764,16 +764,13 @@ export const settingsRouter = createTRPCRouter({
764764
return haveServers.length > 0 || haveProjects.length > 0;
765765
}),
766766
health: publicProcedure.query(async () => {
767-
if (IS_CLOUD) {
768-
try {
769-
await db.execute(sql`SELECT 1`);
770-
return { status: "ok" };
771-
} catch (error) {
772-
console.error("Database connection error:", error);
773-
throw error;
774-
}
767+
try {
768+
await db.execute(sql`SELECT 1`);
769+
return { status: "ok" };
770+
} catch (error) {
771+
console.error("Database connection error:", error);
772+
throw error;
775773
}
776-
return { status: "not_cloud" };
777774
}),
778775
setupGPU: adminProcedure
779776
.input(

apps/dokploy/wait-for-postgres.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import net from "node:net";
2+
import { URL } from "node:url";
3+
import { dbUrl } from "@dokploy/server/db/constants";
4+
5+
const TIMEOUT_MS = Number(process.env.POSTGRES_WAIT_TIMEOUT || 120_000);
6+
const RETRY_DELAY_MS = Number(process.env.POSTGRES_WAIT_RETRY || 2000);
7+
8+
function sleep(ms: number) {
9+
return new Promise((resolve) => setTimeout(resolve, ms));
10+
}
11+
12+
function resolvePostgresTarget(): { host: string; port: number } {
13+
const databaseUrl = dbUrl;
14+
15+
if (!databaseUrl) {
16+
console.error("[wait-for-postgres] DATABASE_URL is not set");
17+
process.exit(1);
18+
}
19+
20+
try {
21+
const url = new URL(databaseUrl);
22+
23+
const host = url.hostname;
24+
const port = Number(url.port || 5432);
25+
26+
if (!host) {
27+
throw new Error("DATABASE_URL has no hostname");
28+
}
29+
30+
return { host, port };
31+
} catch (err) {
32+
console.error("[wait-for-postgres] Invalid DATABASE_URL:", databaseUrl);
33+
process.exit(1);
34+
}
35+
}
36+
37+
function checkTcpConnection(host: string, port: number): Promise<void> {
38+
return new Promise((resolve, reject) => {
39+
const socket = net.createConnection({ host, port });
40+
41+
socket.setTimeout(3000);
42+
43+
socket.on("connect", () => {
44+
socket.end();
45+
resolve();
46+
});
47+
48+
socket.on("timeout", () => {
49+
socket.destroy();
50+
reject(new Error("Connection timeout"));
51+
});
52+
53+
socket.on("error", reject);
54+
});
55+
}
56+
57+
async function waitForPostgres() {
58+
const { host, port } = resolvePostgresTarget();
59+
const start = Date.now();
60+
61+
console.log(
62+
`[wait-for-postgres] Waiting for postgres at ${host}:${port} (timeout ${TIMEOUT_MS}ms)`,
63+
);
64+
65+
while (true) {
66+
try {
67+
await checkTcpConnection(host, port);
68+
console.log("[wait-for-postgres] Postgres is reachable ✅");
69+
return;
70+
} catch {
71+
const elapsed = Date.now() - start;
72+
73+
if (elapsed > TIMEOUT_MS) {
74+
console.error(
75+
`[wait-for-postgres] Timeout after ${elapsed}ms. Postgres not reachable ❌`,
76+
);
77+
process.exit(1);
78+
}
79+
80+
console.log(
81+
`[wait-for-postgres] Postgres not ready yet, retrying in ${RETRY_DELAY_MS}ms...`,
82+
);
83+
await sleep(RETRY_DELAY_MS);
84+
}
85+
}
86+
}
87+
88+
waitForPostgres().catch((err) => {
89+
console.error("[wait-for-postgres] Fatal error:", err);
90+
process.exit(1);
91+
});

0 commit comments

Comments
 (0)