diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 90c7c4528e8..46517c6b092 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -256,6 +256,7 @@ bool		remove_temp_files_after_crash = true;
  */
 bool		send_abort_for_crash = false;
 bool		send_abort_for_kill = false;
+bool		preallocate_stack = false;
 
 /* special child processes; NULL when not running */
 static PMChild *StartupPMChild = NULL,
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index dbef734a93f..21775456c35 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3619,6 +3619,106 @@ ProcessInterrupts(void)
 		ProcessRepackMessages();
 }
 
+#ifndef WIN32
+/*
+ * occupy_stack - recursively touch stack pages to force physical allocation
+ *
+ * Each frame allocates 256kB (half of STACK_DEPTH_SLOP). Recursion stops
+ * when stack_is_too_deep() fires.
+ *
+ * Returns the number of frames successfully allocated.
+ */
+static pg_noinline int
+occupy_stack(void)
+{
+	/* Occupy half of STACK_DEPTH_SLOP at once */
+	volatile char	stack_data[256 * 1024];
+	volatile char	*p = stack_data;
+
+	/* Touch each page to force kernel to back it with physical memory */
+	for (long i = 0; i < sizeof(stack_data); i += 4096)
+		p[i] = 0;
+
+	if (!stack_is_too_deep())
+		return occupy_stack() + 1;
+	return 0;
+}
+
+static sigjmp_buf alt_sigsegv_jump;
+
+/* SIGSEGV handler for allocate_stack() */
+#if defined(USE_SIGACTION) && defined(USE_SIGINFO)
+static void
+alt_sigsegv_handler(int postgres_signal_arg, siginfo_t *info, void *context)
+#else                           /* no USE_SIGINFO */
+static void
+alt_sigsegv_handler(int postgres_signal_arg)
+#endif
+{
+	siglongjmp(alt_sigsegv_jump, 1);
+}
+#endif
+
+/*
+ * allocate_stack - acquire stack pages to prevent SIGSEGV under memory pressure
+ *
+ * When the system is under memory pressure (e.g., cgroup limits), the kernel
+ * may fail to extend the stack on demand, killing the backend with SIGSEGV
+ * before check_stack_depth() can intervene.  This function proactively acquires
+ * physical pages of stack up to max_stack_depth via occupy_stack(), while it
+ * can still handle failures gracefully, using a temporary SIGSEGV handler on
+ * an alternate signal stack.
+ *
+ * Returns the number of frames successfully allocated (0 on Windows).
+ */
+int
+allocate_stack(void)
+{
+	int			result = 0;
+
+#ifndef WIN32
+	struct sigaction	act, oldact;
+	stack_t			ss, old_ss;
+
+	ss.ss_sp = palloc(SIGSTKSZ);
+	ss.ss_size = SIGSTKSZ;
+	ss.ss_flags = 0;
+	if (sigaltstack(&ss, &old_ss) == -1)
+		elog(FATAL, "sigaltstack failed: %m");
+
+	act.sa_flags = SA_ONSTACK;
+	act.sa_handler = alt_sigsegv_handler;
+	sigemptyset(&act.sa_mask);
+	if (sigaction(SIGSEGV, &act, &oldact) == -1)
+		elog(FATAL, "sigaction failed: %m");
+
+	/*
+	 * Use sigsetjmp to intercept SIGSEGV that can happen even at the process
+	 * start, and in this case occupy_stack() can't handle it.
+	 *
+	 * Terminating the process with FATAL is the only way to avoid
+	 * segfaults later.
+	 */
+	if (sigsetjmp(alt_sigsegv_jump, 1) == 0)
+	{
+		result = occupy_stack();
+	}
+	else
+	{
+		/* We got the SIGSEGV trap */
+		elog(FATAL, "could not allocate stack");
+	}
+
+	if (sigaltstack(&old_ss, NULL) == -1)
+		elog(FATAL, "sigaltstack restore failed: %m");
+	if (sigaction(SIGSEGV, &oldact, NULL) == -1)
+		elog(FATAL, "sigaction failed: %m");
+	pfree(ss.ss_sp);
+#endif
+
+	return result;
+}
+
 /*
  * GUC check_hook for client_connection_check_interval
  */
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 7ffc808073a..a0dd7c6f901 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -107,6 +107,9 @@ InitPostmasterChild(void)
 	pgwin32_signal_initialize();
 #endif
 
+	if (preallocate_stack)
+		(void) allocate_stack();
+
 	InitProcessGlobals();
 
 	/*
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index afaa058b046..b2d5642f572 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2390,6 +2390,12 @@
   max => '60',
 },
 
+{ name => 'preallocate_stack', type => 'bool', context => 'PGC_POSTMASTER', group => 'RESOURCES_MEM',
+  short_desc => 'Preallocate stack on subprocess start.',
+  variable => 'preallocate_stack',
+  boot_val => 'false',
+},
+
 { name => 'primary_conninfo', type => 'string', context => 'PGC_SIGHUP', group => 'REPLICATION_STANDBY',
   short_desc => 'Sets the connection string to be used to connect to the sending server.',
   flags => 'GUC_SUPERUSER_ONLY',
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index ac38cddaaf9..0c112f51684 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -150,6 +150,7 @@
 #autovacuum_work_mem = -1               # min 64kB, or -1 to use maintenance_work_mem
 #logical_decoding_work_mem = 64MB       # min 64kB
 #max_stack_depth = 2MB                  # min 100kB
+#preallocate_stack = off                # allocate max_stack_depth on subprocess start
 #shared_memory_type = mmap              # the default is the first option
                                         # supported by the operating system:
                                         #   mmap
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 8ccdf61246b..b0efa042ec5 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -306,6 +306,7 @@ extern void restore_stack_base(pg_stack_base_t base);
 extern void check_stack_depth(void);
 extern bool stack_is_too_deep(void);
 extern ssize_t get_stack_depth_rlimit(void);
+extern int allocate_stack(void);
 
 /* in tcop/utility.c */
 extern void PreventCommandIfReadOnly(const char *cmdname);
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 716b4c912b3..7898c021b12 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -70,6 +70,7 @@ extern PGDLLIMPORT bool restart_after_crash;
 extern PGDLLIMPORT bool remove_temp_files_after_crash;
 extern PGDLLIMPORT bool send_abort_for_crash;
 extern PGDLLIMPORT bool send_abort_for_kill;
+extern PGDLLIMPORT bool preallocate_stack;
 
 #ifdef WIN32
 extern PGDLLIMPORT HANDLE PostmasterHandle;
