From b4b570956d7dac39c8a153ca83521c63a37f421d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Salo=C5=88?= Date: Fri, 18 Sep 2026 14:06:05 +0200 Subject: [PATCH 1/3] z0 - added more PostgreSQL info --- .../content/postgresql/how-to/connect.mdx | 35 +++++++++++ apps/docs/content/postgresql/how-to/scale.mdx | 61 ++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/apps/docs/content/postgresql/how-to/connect.mdx b/apps/docs/content/postgresql/how-to/connect.mdx index 2fbda2e6..04839925 100644 --- a/apps/docs/content/postgresql/how-to/connect.mdx +++ b/apps/docs/content/postgresql/how-to/connect.mdx @@ -127,6 +127,41 @@ A few things to know: - **TLS is required** on `6432` (see [above](#connection-ports-and-tls)), even for internal connections. - **HA mode.** pgBouncer pools connections to the primary (writes). Read routing across replicas on port `5433` is separate and is not pooled. +## Connection limits + +`max_connections` follows the service's RAM and [workload type](/postgresql/how-to/scale#workload-types): 50 per GiB (between 20 and 500) for OLTP and WriteHeavy, 25 per GiB (between 10 and 200) for OLAP. RAM counts in [memory steps](/postgresql/how-to/scale#how-postgresql-scaling-works), so a service with 7 GB of RAM still gets the `4 GiB` limits. pgBouncer accepts `20 × (max_connections − 4)` clients, at least 100. + + + + + + + + + + + + + + + + + + + + + + + + +
Memory stepmax_connections (5432, 5433)pgBouncer clients (6432)
OLTP / WriteHeavyOLAPOLTP / WriteHeavyOLAP
256 MiB2010320120
512 MiB2512420160
1 GiB5025920420
2 GiB100501920920
4 GiB20010039201920
8 GiB40020079203920
16 GiB and up50020099203920
+ +- **Not all of `max_connections` is yours.** 3 connections are reserved for superusers, and Zerops itself uses up to 4 (monitoring, health checks, backups). +- **pgBouncer's server-side pool is much smaller than its client limit.** Each user/database pair gets up to `2 × vCPU + 1` server connections (the database's CPU cores), and each database at most a third of `max_connections`. Transactions beyond that wait in a queue instead of failing. +- **The limits cannot be overridden.** If you need more connections, connect through `6432`, or raise the minimum RAM to the next memory step. +- **HA mode.** The limits apply per node. Port `5433` balances across both replicas, so reads get twice the `max_connections`. Each of the two proxies runs its own pgBouncer with the full client limit, but both pool into the same primary: with several busy databases they can still exhaust its `max_connections`. +- **Idle connections in HA mode.** The proxies close connections on `5432` and `5433` that stay idle for 60 minutes. + ## Connect from services in the same project All services in a project share a private network, so other services reach PostgreSQL directly by its hostname. There are two ways to wire it up. diff --git a/apps/docs/content/postgresql/how-to/scale.mdx b/apps/docs/content/postgresql/how-to/scale.mdx index 1301a103..47c77cc3 100644 --- a/apps/docs/content/postgresql/how-to/scale.mdx +++ b/apps/docs/content/postgresql/how-to/scale.mdx @@ -20,7 +20,7 @@ PostgreSQL services use **vertical scaling** to adjust CPU, RAM, and disk resour :::danger Scaling can briefly interrupt the service When scaling changes the service's resources, Zerops regenerates the PostgreSQL configuration and applies it with an automatic **reload**. If the new values require it, the service is **restarted** instead: rolling through the cluster in HA mode, a short outage in single mode. -A restart is only needed when the granted RAM crosses a memory step: `256 MiB`, `512 MiB`, `1 GiB`, `2 GiB`, `4 GiB`, then multiples of `8 GiB`. Scaling within a step reloads only; to rule out restarts entirely, keep `minRam` and `maxRam` within one step. +A restart is only needed when the granted RAM crosses a memory step: `256 MiB`, `512 MiB`, `1 GiB`, `2 GiB`, `4 GiB`, then multiples of `8 GiB`. Scaling within a step reloads only; to rule out restarts entirely, keep `minRam` and `maxRam` within one step. Crossing a step also changes the [connection limits](/postgresql/how-to/connect#connection-limits). ::: ## Scaling profiles @@ -61,6 +61,63 @@ A profile name combines a **workload type** with a **tier**, e.g. `oltp-producti +The settings you are most likely to run into (RAM means the current [memory step](#how-postgresql-scaling-works)): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SettingOLTPOLAPWriteHeavy
max_connectionsSee Connection limits
shared_buffers25% of RAM, max 8 GiB25% of RAM, max 16 GiB25% of RAM, max 8 GiB
effective_cache_size75% of RAM75% of RAM50% of RAM
work_memRAM / (max_connections × 4): about 5 MiB up to 8 GiB of RAM, 25 MiB at 48 GiBRAM / (max_connections × 2): about 20 MiB up to 8 GiB of RAM, 123 MiB at 48 GiBRAM / (max_connections × 8), at least 4 MiB: 4 MiB up to 16 GiB of RAM, 12 MiB at 48 GiB
temp_file_limit2 × RAM, max 48 GiB. A query that needs more temporary disk space fails.
idle_in_transaction_session_timeout5 minutes10 minutes5 minutes
jitOn from 4 GiB of RAMOnOff
+
+ +`work_mem` is sized so that every connection can run several sorts at once without exhausting RAM. If a few heavy queries need more, raise it just for them on any profile: `ALTER ROLE ... SET work_mem` for a dedicated role, or `SET LOCAL work_mem` inside a transaction. + ### Available profiles The tier part of the name sets the size of the autoscaling envelope (and, in HA, the replication topology). Which profiles you can pick depends on the deployment mode: @@ -92,7 +149,7 @@ The tier part of the name sets the size of the autoscaling envelope (and, in HA, oltp-enterprise HA only - High-throughput OLTP at scale. Highest connection limits and the most aggressive headroom. + High-throughput OLTP at scale. The largest resource envelope and the most aggressive headroom. olap-production From b0dd21cb18dea4372af0662fc6869d66c6c0a12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Salo=C5=88?= Date: Fri, 18 Sep 2026 15:12:56 +0200 Subject: [PATCH 2/3] z0 - expand overflowing code blocks on hover --- apps/docs/src/components/Dropdown/index.tsx | 5 +- apps/docs/src/css/_docusaurus.css | 79 ++++++++++++++++--- .../src/theme/CodeBlock/Content/String.tsx | 1 + 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/apps/docs/src/components/Dropdown/index.tsx b/apps/docs/src/components/Dropdown/index.tsx index d98848fd..5ee53534 100644 --- a/apps/docs/src/components/Dropdown/index.tsx +++ b/apps/docs/src/components/Dropdown/index.tsx @@ -22,7 +22,7 @@ export function DropdownItem({ title, children, wrap = true }: DropdownItemProps return (
-
+
diff --git a/apps/docs/src/css/_docusaurus.css b/apps/docs/src/css/_docusaurus.css index 29eb6132..135f8feb 100644 --- a/apps/docs/src/css/_docusaurus.css +++ b/apps/docs/src/css/_docusaurus.css @@ -62,16 +62,21 @@ details + p { @apply relative mb-4 bg-medusa-bg-subtle dark:bg-medusa-bg-base hover:bg-medusa-bg-subtle-hover dark:hover:bg-medusa-bg-base-hover; } -.col.toc-wrapper { - --ifm-col-width: 220px; +:root { + --doc-toc-width: 220px; + --code-block-actions-width: 68px; } @media (min-width: 1400px) { - .col.toc-wrapper { - --ifm-col-width: 300px; + :root { + --doc-toc-width: 300px; } } +.col.toc-wrapper { + --ifm-col-width: var(--doc-toc-width); +} + .markdown-doc-wrapper--fluid { @apply max-w-[inherit]; } @@ -92,19 +97,69 @@ details + p { @apply text-medusa-code-text-subtle !pl-0; } +/* The action buttons get a fixed-width gutter as a margin (not a percentage max-width), so it stays the same size when the block expands + and it counts towards the max-content width the expanded block sizes itself to. */ .prism-code { - @apply xs:max-w-[90%] text-code-body [&_*]:text-code-body xs:after:content-[''] xs:after:rounded xs:after:absolute; - @apply xs:after:right-0 xs:after:top-0 xs:after:w-[calc(10%+24px)] xs:after:h-full xs:after:bg-code-fade; + @apply text-code-body [&_*]:text-code-body xs:after:content-[''] xs:after:rounded xs:after:absolute; + @apply xs:after:right-0 xs:after:top-0 xs:after:h-full xs:after:bg-code-fade; + @apply xs:after:w-[calc(var(--code-block-actions-width)+24px)] xs:after:pointer-events-none; +} + +/* The extra class in the selector is there to outrank the m-0 utility on the pre, which comes later in the stylesheet. */ +@media (min-width: 576px) { + .theme-code-block .prism-code { + margin-right: var(--code-block-actions-width); + } +} + +/* Touch devices never hover, so hiding the thumb there would leave their scroll indicator permanently invisible. */ +@media (hover: hover) and (pointer: fine) { + .prism-code { + scrollbar-color: transparent transparent; + } + + .theme-code-block:hover .prism-code, + .prism-code:focus-visible { + scrollbar-color: var(--ifm-scrollbar-thumb-background-color) transparent; + } } -.prism-code:not(:hover)::-webkit-scrollbar-thumb, -.prism-code:not(:hover)::-webkit-scrollbar-track { - @apply xs:invisible; +/* A permanent scrollbar keeps the block height the same whether the code overflows (collapsed) or fits (expanded). + With the track transparent it only shows as a thumb, which is absent when there is nothing to scroll. */ +.code-block-scrollable .prism-code { + overflow-x: scroll; } -.prism-code:hover::-webkit-scrollbar-thumb, -.prism-code:hover::-webkit-scrollbar-track { - @apply xs:opacity-100; +/* Overflowing code blocks expand over the TOC column on hover. Needs a pointer that can hover and a TOC column to expand into. */ +@media (hover: hover) and (pointer: fine) and (min-width: 997px) { + .row:has(> .toc-wrapper) .theme-code-block.code-block-scrollable:not(.reference-table *) { + position: relative; + width: max-content; + min-width: 100%; + max-width: 100%; + /* z-index is held until the collapse animation ends, otherwise the sticky TOC paints over the shrinking block. + The resting value has to be a number for that, because a change from auto cannot be delayed by a transition. */ + z-index: 0; + transition: max-width 120ms ease-out, box-shadow 120ms ease-out, z-index 0s 120ms; + } + + /* :active keeps the block open while a text selection drag leaves it. :focus-visible (not :focus-within) so a mouse click does not pin it. */ + .row:has(> .toc-wrapper) .theme-code-block.code-block-scrollable:not(.reference-table *):is(:hover, :active, :has(:focus-visible)) { + /* Reaches the right edge of the TOC content: article and column paddings (32px) plus the column's auto margin (13.5px on the fixed-width + layout from 1419px up) plus the TOC column minus its own right padding (16px). Narrower viewports have a smaller margin, where the + rounded-up 30px ends inside the TOC column's right padding. */ + max-width: calc(100% + var(--doc-toc-width) + 30px); + z-index: 99; + /* The delay stops blocks from popping open as they pass under the cursor while the page scrolls. */ + transition: max-width 120ms ease-out 180ms, box-shadow 120ms ease-out 180ms, z-index 0s; + @apply shadow-flyout dark:shadow-flyout-dark; + } +} + +@media (prefers-reduced-motion: reduce) { + .theme-code-block.code-block-scrollable { + transition-duration: 0s !important; + } } .prism-code { diff --git a/apps/docs/src/theme/CodeBlock/Content/String.tsx b/apps/docs/src/theme/CodeBlock/Content/String.tsx index 8dc84bc0..22e413ef 100644 --- a/apps/docs/src/theme/CodeBlock/Content/String.tsx +++ b/apps/docs/src/theme/CodeBlock/Content/String.tsx @@ -65,6 +65,7 @@ export default function CodeBlockString({ as="div" className={clsx( blockClassName, + wordWrap.isCodeScrollable && 'code-block-scrollable', language && !blockClassName.includes(`language-${language}`) && `language-${language}` From 353c004bcd6711971b69388ad4a6ecde2855cacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Salo=C5=88?= Date: Fri, 18 Sep 2026 15:28:54 +0200 Subject: [PATCH 3/3] add cache to build process --- zerops.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zerops.yml b/zerops.yml index d7f0b477..103155a3 100644 --- a/zerops.yml +++ b/zerops.yml @@ -14,6 +14,9 @@ zerops: - yarn build deployFiles: - apps/docs/build/~ + cache: + - ./node_modules/ + - ./apps/docs/node_modules/ run: base: static initCommands: