feat(dokploy): add wait-for-postgres script and update Dockerfile and package.json

- Introduced a new script to wait for PostgreSQL to be ready before starting the application.
- Updated the Dockerfile to include a health check for the application.
- Modified the start script in package.json to run the wait-for-postgres script prior to starting the server and migration processes.
- Added the wait-for-postgres TypeScript file to handle connection retries to the PostgreSQL database.
This commit is contained in:
Mauricio Siu
2026-02-08 15:43:58 -06:00
parent 08ba24c252
commit f2e4a96154
5 changed files with 106 additions and 11 deletions
+5 -1
View File
@@ -65,4 +65,8 @@ RUN curl -sSL https://railpack.com/install.sh | bash
COPY --from=buildpacksio/pack:0.39.1 /usr/local/bin/pack /usr/local/bin/pack
EXPOSE 3000
CMD [ "pnpm", "start" ]
HEALTHCHECK --interval=10s --timeout=3s --retries=10 \
CMD curl -fs http://localhost:3000/api/trpc/settings.health || exit 1
CMD ["sh", "-c", "pnpm run wait-for-postgres && exec pnpm start"]
+1
View File
@@ -25,6 +25,7 @@ try {
entryPoints: {
server: "server/server.ts",
migration: "migration.ts",
"wait-for-postgres": "wait-for-postgres.ts",
"reset-password": "reset-password.ts",
"reset-2fa": "reset-2fa.ts",
},
+3 -1
View File
@@ -6,10 +6,12 @@
"type": "module",
"scripts": {
"build": "npm run build-server && npm run build-next",
"start": "node -r dotenv/config dist/migration.mjs && node -r dotenv/config dist/server.mjs",
"start": "node -r dotenv/config dist/wait-for-postgres.mjs && node -r dotenv/config dist/migration.mjs && node -r dotenv/config dist/server.mjs",
"build-server": "tsx esbuild.config.ts",
"build-next": "next build --webpack",
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
"wait-for-postgres": "node -r dotenv/config dist/wait-for-postgres.mjs",
"wait-for-postgres-dev": "tsx -r dotenv/config wait-for-postgres.ts",
"reset-password": "node -r dotenv/config dist/reset-password.mjs",
"reset-2fa": "node -r dotenv/config dist/reset-2fa.mjs",
"dev": "tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ",
+6 -9
View File
@@ -764,16 +764,13 @@ export const settingsRouter = createTRPCRouter({
return haveServers.length > 0 || haveProjects.length > 0;
}),
health: publicProcedure.query(async () => {
if (IS_CLOUD) {
try {
await db.execute(sql`SELECT 1`);
return { status: "ok" };
} catch (error) {
console.error("Database connection error:", error);
throw error;
}
try {
await db.execute(sql`SELECT 1`);
return { status: "ok" };
} catch (error) {
console.error("Database connection error:", error);
throw error;
}
return { status: "not_cloud" };
}),
setupGPU: adminProcedure
.input(
+91
View File
@@ -0,0 +1,91 @@
import net from "node:net";
import { URL } from "node:url";
import { dbUrl } from "@dokploy/server/db/constants";
const TIMEOUT_MS = Number(process.env.POSTGRES_WAIT_TIMEOUT || 120_000);
const RETRY_DELAY_MS = Number(process.env.POSTGRES_WAIT_RETRY || 2000);
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function resolvePostgresTarget(): { host: string; port: number } {
const databaseUrl = dbUrl;
if (!databaseUrl) {
console.error("[wait-for-postgres] DATABASE_URL is not set");
process.exit(1);
}
try {
const url = new URL(databaseUrl);
const host = url.hostname;
const port = Number(url.port || 5432);
if (!host) {
throw new Error("DATABASE_URL has no hostname");
}
return { host, port };
} catch (err) {
console.error("[wait-for-postgres] Invalid DATABASE_URL:", databaseUrl);
process.exit(1);
}
}
function checkTcpConnection(host: string, port: number): Promise<void> {
return new Promise((resolve, reject) => {
const socket = net.createConnection({ host, port });
socket.setTimeout(3000);
socket.on("connect", () => {
socket.end();
resolve();
});
socket.on("timeout", () => {
socket.destroy();
reject(new Error("Connection timeout"));
});
socket.on("error", reject);
});
}
async function waitForPostgres() {
const { host, port } = resolvePostgresTarget();
const start = Date.now();
console.log(
`[wait-for-postgres] Waiting for postgres at ${host}:${port} (timeout ${TIMEOUT_MS}ms)`,
);
while (true) {
try {
await checkTcpConnection(host, port);
console.log("[wait-for-postgres] Postgres is reachable ✅");
return;
} catch {
const elapsed = Date.now() - start;
if (elapsed > TIMEOUT_MS) {
console.error(
`[wait-for-postgres] Timeout after ${elapsed}ms. Postgres not reachable ❌`,
);
process.exit(1);
}
console.log(
`[wait-for-postgres] Postgres not ready yet, retrying in ${RETRY_DELAY_MS}ms...`,
);
await sleep(RETRY_DELAY_MS);
}
}
}
waitForPostgres().catch((err) => {
console.error("[wait-for-postgres] Fatal error:", err);
process.exit(1);
});