<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bytebase</title>
    <description>The latest articles on DEV Community by Bytebase (bytebase).</description>
    <link>https://dev.to/bytebase</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F5927%2F4381e297-7491-4188-919f-f194d6f90aaa.png</url>
      <title>DEV Community: Bytebase</title>
      <link>https://dev.to/bytebase</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bytebase"/>
    <language>en</language>
    <item>
      <title>DBHub vs Bytebase MCP: which one to point your agent at</title>
      <dc:creator>Adela</dc:creator>
      <pubDate>Thu, 13 Aug 2026 13:16:33 +0000</pubDate>
      <link>https://dev.to/bytebase/dbhub-vs-bytebase-mcp-which-one-to-point-your-agent-at-ana</link>
      <guid>https://dev.to/bytebase/dbhub-vs-bytebase-mcp-which-one-to-point-your-agent-at-ana</guid>
      <description>&lt;p&gt;We maintain two database MCP servers, &lt;a href="https://github.com/bytebase/dbhub" rel="noopener noreferrer"&gt;DBHub&lt;/a&gt; and the one built into &lt;a href="https://github.com/bytebase/bytebase" rel="noopener noreferrer"&gt;Bytebase&lt;/a&gt;. People keep asking which to use, so: &lt;strong&gt;DBHub when you're the only one affected, Bytebase MCP when other people are.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Note that's not the same as dev vs prod. A read-only replica only you touch is fine on DBHub; a staging database loaded with a copy of real customer data isn't. What matters is who pays if the agent does the worst thing it can do.&lt;/p&gt;

&lt;p&gt;The rest is one difference, and everything follows from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared account vs your account
&lt;/h2&gt;

&lt;p&gt;DBHub takes a connection string. In practice that's a shared service account, usually the one your app already uses, and every agent pointed at it connects as that same account.&lt;/p&gt;

&lt;p&gt;Bytebase MCP takes an OAuth login. The agent connects as you.&lt;/p&gt;

&lt;p&gt;So when your agent runs a &lt;code&gt;DELETE&lt;/code&gt; at 2am, DBHub's database logs &lt;code&gt;app_user&lt;/code&gt;, same as every other query from everyone else sharing it. You know it happened, not who it happened for, and you can't revoke it for one person without revoking it for everybody. Bytebase logs it under your account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9hx7zntspvhe2d7rjd5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9hx7zntspvhe2d7rjd5c.png" alt="Two lanes with the same two people and the same Postgres database. Through DBHub both identities stop at the server and are replaced by one shared app_user, so the Postgres log shows the same name twice. Through Bytebase MCP both identities are carried through, so the Bytebase audit shows alice@corp and bob@corp separately along with what each one did." width="799" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  DBHub
&lt;/h2&gt;

&lt;p&gt;One command, no server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @bytebase/dbhub@latest &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="nt"&gt;--dsn&lt;/span&gt; &lt;span class="s2"&gt;"postgres://user:pass@localhost:5432/mydb"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two tools by default, run SQL and search the schema, which keeps your context window free for the actual problem. Postgres, MySQL, MariaDB, SQL Server and SQLite, several connections in one process. You can start it read-only, cap the rows, set a query timeout.&lt;/p&gt;

&lt;p&gt;No identity, though. Read-only is a flag on the process, so it protects you from the agent, not from whoever started the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bytebase MCP
&lt;/h2&gt;

&lt;p&gt;Nothing to install. If you run Bytebase it's at &lt;code&gt;/mcp&lt;/code&gt;, and your agent inherits your account, which already has rules on it: masked columns come back &lt;code&gt;******&lt;/code&gt;, schema changes open a review instead of running, and every call is logged under your name.&lt;/p&gt;

&lt;p&gt;Worth saying plainly: it inherits &lt;em&gt;all&lt;/em&gt; your permissions. Connecting as yourself only helps if yourself isn't an admin.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DBHub&lt;/th&gt;
&lt;th&gt;Bytebase MCP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Connects as&lt;/td&gt;
&lt;td&gt;A shared account&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reads&lt;/td&gt;
&lt;td&gt;As stored&lt;/td&gt;
&lt;td&gt;Masked per policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writes&lt;/td&gt;
&lt;td&gt;Run immediately&lt;/td&gt;
&lt;td&gt;Open a review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit&lt;/td&gt;
&lt;td&gt;Database login records&lt;/td&gt;
&lt;td&gt;Every call, under your account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;A running Bytebase&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The catches
&lt;/h2&gt;

&lt;p&gt;DBHub has no per-person access to grant or revoke, no masking, and nothing between the agent and the table if writes are on.&lt;/p&gt;

&lt;p&gt;Bytebase MCP needs Bytebase running with an external URL, and it's HTTP only. You can't dry-run SQL, since the checks happen when the change is created. Results cap at 100 rows by default, 1,000 max, 30 second timeout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking
&lt;/h2&gt;

&lt;p&gt;Local Postgres, a scratch copy, a replica, read-only poking at prod: DBHub. Real customer data, masked columns, writes someone should look at first: Bytebase MCP.&lt;/p&gt;

&lt;p&gt;Both, honestly, is the normal answer. DBHub in your client config for local work, Bytebase MCP for anything shared. What to avoid is the middle, where a connection string that happens to reach production ends up in an MCP config because it was the fastest thing to paste.&lt;/p&gt;

</description>
      <category>database</category>
      <category>ai</category>
      <category>postgres</category>
      <category>devops</category>
    </item>
    <item>
      <title>🐘🤖 Top 3 Open Source Postgres MCP Servers in 2026</title>
      <dc:creator>Adela</dc:creator>
      <pubDate>Tue, 28 Jul 2026 12:35:02 +0000</pubDate>
      <link>https://dev.to/bytebase/top-3-open-source-postgres-mcp-servers-in-2026-43g4</link>
      <guid>https://dev.to/bytebase/top-3-open-source-postgres-mcp-servers-in-2026-43g4</guid>
      <description>&lt;p&gt;At Bytebase we work on database access control all day, so pointing an AI agent at a real database is not a hypothetical for me. Picking a Postgres MCP server is easy right up until the database on the other end holds something you actually care about. What runs fine against a throwaway local database behaves very differently in production. That split, trusted local development vs. production, is the lens I use here. Most of these servers are great at the first and quietly dangerous at the second.&lt;/p&gt;

&lt;p&gt;Building one safely is harder than it looks. Anthropic's own reference Postgres server got archived after people noticed its "read-only" mode happily accepted &lt;code&gt;COMMIT; DROP SCHEMA public CASCADE;&lt;/code&gt;. Read-only, except when it wasn't. So I care less about the feature list and more about two questions: what can the agent actually do, and can you prove afterward what it did. The three servers below answer those very differently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://star-history.com/#crystaldba/postgres-mcp&amp;amp;supabase/mcp&amp;amp;pgplex/pgconsole&amp;amp;Date" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwsrv.nl%2F%3Furl%3Dwww.bytebase.com%2Fcontent%2Fblog%2Ftop-open-source-postgres-mcp-servers%2Fstar-history.svg%26output%3Dpng%26w%3D1200" alt="Postgres MCP server GitHub star history" width="1200" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/pgplex/pgconsole" rel="noopener noreferrer"&gt;pgconsole&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;pgconsole is the newest of the three. It is a self-hosted Postgres console, a web SQL editor first, that also hands its connections to agents over a built-in MCP endpoint. The important part: the agent never sees a connection string. It gets a token, and every call runs through the same access rules and audit log as a human clicking around the console. The agent talks to pgconsole, and pgconsole talks to the database.&lt;/p&gt;

&lt;p&gt;Most database tools land at one of two extremes. Heavyweight platforms that need real infrastructure to stand up, or lightweight editors with no access controls at all. pgconsole sits in the awkward, useful middle: a Postgres editor with team controls built in, shipped as a single binary you run yourself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwsrv.nl%2F%3Furl%3Dwww.bytebase.com%2Fcontent%2Fblog%2Ftop-open-source-postgres-mcp-servers%2Fpgconsole.svg%26output%3Dpng%26w%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwsrv.nl%2F%3Furl%3Dwww.bytebase.com%2Fcontent%2Fblog%2Ftop-open-source-postgres-mcp-servers%2Fpgconsole.svg%26output%3Dpng%26w%3D1200" alt="pgconsole gives the agent its own identity, acting on behalf of a user with rights attenuated to least privilege" width="1200" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The access control is the heart of it, and it treats agents as first-class principals instead of bolting them on after the fact with built-in delegation and attenuation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One self-hosted binary. Connections, users, and access rules live in a versioned &lt;code&gt;pgconsole.toml&lt;/code&gt;, GitOps-style, and you run it with npx or Docker. No separate database to babysit.&lt;/li&gt;
&lt;li&gt;Fine-grained, default-deny access. Seven independent rights (read, write, DDL, admin, and more), scoped per connection, with nothing allowed unless a rule grants it. pgconsole parses every statement before it runs, so a destructive query cannot sneak through a read-only tool.&lt;/li&gt;
&lt;li&gt;Agents as first-class identities. An agent authenticates as its own principal, or acts on behalf of a user. In delegated mode it inherits that person's permissions, capped lower and never higher, and it loses access the moment the user does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;alice-claude&lt;/code&gt; agent acts for Alice, capped to read-only on a single connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[agents]]&lt;/span&gt;
&lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"alice-claude"&lt;/span&gt;
&lt;span class="py"&gt;on_behalf_of&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"alice@example.com"&lt;/span&gt;
&lt;span class="py"&gt;permissions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c"&gt;# capped at or below Alice's own grants&lt;/span&gt;
&lt;span class="py"&gt;connections&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"prod"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c"&gt;# and scoped to a single connection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; teams that want a self-hosted Postgres editor and a genuinely governed agent path (distinct identities, delegation, least-privilege caps) without standing up a whole platform.&lt;/p&gt;

&lt;p&gt;One honest caveat: it is young. Around 120 stars, Apache-2.0, recent first releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; the access model is the reason to run it. Distinct agent identity, on-behalf-of delegation, and attenuation that holds an agent at or below the human it acts for, all enforced by a SQL parser rather than by trust. If you remember one design from this post, make it this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/supabase/mcp" rel="noopener noreferrer"&gt;Supabase MCP&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Supabase MCP manages a whole Supabase project: auth, storage, edge functions, branches, not just the Postgres database underneath. That breadth is both the appeal and the cage. It only works on Supabase.&lt;/p&gt;

&lt;p&gt;It is a first-party server from Supabase, the open-source Firebase alternative, and it inherits the company's instinct to treat everything as one managed surface. That is why it reaches well past plain SQL into auth, storage, and functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwsrv.nl%2F%3Furl%3Dwww.bytebase.com%2Fcontent%2Fblog%2Ftop-open-source-postgres-mcp-servers%2Fsupabase.svg%26output%3Dpng%26w%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwsrv.nl%2F%3Furl%3Dwww.bytebase.com%2Fcontent%2Fblog%2Ftop-open-source-postgres-mcp-servers%2Fsupabase.svg%26output%3Dpng%26w%3D1200" alt="Supabase MCP exposes the whole project surface, but only for Supabase-hosted databases" width="1200" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wide surface: 29 tools across eight feature groups (Account, Database, Debugging, Development, Edge Functions, Branching, Docs, Storage), covering migrations, TypeScript type generation, and serverless functions beyond plain SQL.&lt;/li&gt;
&lt;li&gt;Hosted, with OAuth 2.1 and automatic token refresh, so no local server, binaries, or hardcoded credentials.&lt;/li&gt;
&lt;li&gt;Stronger security defaults than most: project scoping, feature-group restriction, and a dedicated read-only role (&lt;code&gt;supabase_read_only_user&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; teams already on Supabase who want their agent to manage the project, not just query it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; excellent if Supabase is your platform, irrelevant otherwise. Two things to know: the default tool set is heavy (~19.3k tokens, though selective loading cuts it to ~4.2k), and the Management API endpoint behind &lt;code&gt;execute_sql&lt;/code&gt; is still beta with no row limits. More guardrails than most. But like every server here, it mitigates prompt injection, it does not solve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/crystaldba/postgres-mcp" rel="noopener noreferrer"&gt;Postgres MCP Pro&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Postgres MCP Pro, from Crystal DBA, is usually the first result when you search for a Postgres MCP server. It is host-neutral, point it at any Postgres, and it goes further than most. Where the others stop at "run this query," it brings a DBA's tuning toolkit.&lt;/p&gt;

&lt;p&gt;Crystal DBA open-sourced it as the on-ramp to its AI-driven Postgres tooling. Then Temporal &lt;a href="https://temporal.io/blog/temporal-and-the-next-frontier-scaling-ai-reliably" rel="noopener noreferrer"&gt;acquired the company&lt;/a&gt; in September 2025, and active development of the server has mostly stalled since. That matters, and I will come back to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwsrv.nl%2F%3Furl%3Dwww.bytebase.com%2Fcontent%2Fblog%2Ftop-open-source-postgres-mcp-servers%2Fpostgres-mcp-pro.svg%26output%3Dpng%26w%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwsrv.nl%2F%3Furl%3Dwww.bytebase.com%2Fcontent%2Fblog%2Ftop-open-source-postgres-mcp-servers%2Fpostgres-mcp-pro.svg%26output%3Dpng%26w%3D1200" alt="Postgres MCP Pro adds tuning analysis, but defaults to unrestricted read/write access" width="1200" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance analysis: index tuning, &lt;code&gt;EXPLAIN&lt;/code&gt; plan inspection, and database health checks. This is the real differentiator.&lt;/li&gt;
&lt;li&gt;Two access modes: unrestricted (full read/write, arbitrary SQL) and restricted (read-only, with query-time limits).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best For:&lt;/strong&gt; developers and DBAs who want the agent to diagnose a slow query or propose an index, not just read rows.&lt;/p&gt;

&lt;p&gt;Two things to weigh before reaching for it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It defaults to unrestricted, and every README example uses it. Out of the box the agent gets full write access and arbitrary SQL. Set &lt;code&gt;--access-mode=restricted&lt;/code&gt; for anything past a dev sandbox. Shipping with the safety off by default is a choice I disagree with.&lt;/li&gt;
&lt;li&gt;Development has stalled since the acquisition. The last tagged release (v0.3.0) was May 2025, and commits trailed off after the September 2025 Temporal deal (the last one landed in January 2026). Merged work like streamable-HTTP transport never made it into a release or the Docker image, &lt;a href="https://github.com/crystaldba/postgres-mcp/issues/162" rel="noopener noreferrer"&gt;people are asking for one&lt;/a&gt;, and 63 issues sit open, including a &lt;a href="https://github.com/crystaldba/postgres-mcp/issues/98" rel="noopener noreferrer"&gt;connection-pool leak under SSE&lt;/a&gt; that is the most-reacted of the bunch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; the tuning features are genuinely good, and still the reason to keep it around for hands-on work. But change the unrestricted default on day one, pin a known-good version, and factor in the stalled maintenance before you let it anywhere near production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Postgres MCP Server Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Stars&lt;/th&gt;
&lt;th&gt;Neutrality&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pgconsole&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;Any Postgres&lt;/td&gt;
&lt;td&gt;Default-deny IAM, per-connection rights; agent identity, on-behalf-of, attenuation, audit&lt;/td&gt;
&lt;td&gt;Self-hosted teams wanting control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supabase MCP&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;2.7k&lt;/td&gt;
&lt;td&gt;Supabase-locked&lt;/td&gt;
&lt;td&gt;Read-only by default, scoped token; project scoping, read-only role&lt;/td&gt;
&lt;td&gt;Teams already on Supabase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postgres MCP Pro&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;2.9k&lt;/td&gt;
&lt;td&gt;Any Postgres&lt;/td&gt;
&lt;td&gt;Read/write by default, opt-in read-only; none (forwards the connection)&lt;/td&gt;
&lt;td&gt;DBA-style tuning + dev&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Star counts are from the time of writing and move fast. Treat them as a rough adoption signal, not a ranking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;None of these three is finished. Pick by where you sit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pgconsole&lt;/strong&gt; has the strongest access model (separate agent identity, on-behalf-of, attenuation) but it is the youngest. Right for a self-hosted editor with a governed agent path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase MCP&lt;/strong&gt; is the most polished, as long as you live on Supabase. Right when you are already on Supabase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postgres MCP Pro&lt;/strong&gt; has the richest toolkit but lost its momentum to an acquisition. Right for diagnosing a slow query in dev, once you switch off the unrestricted default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three are great on a laptop and get scarier in production, for the same structural reason. Simon Willison named it the &lt;a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/" rel="noopener noreferrer"&gt;lethal trifecta&lt;/a&gt;: an agent with access to private data, exposure to untrusted input, and a way to send data back out. A correctly scoped credential does not save you, because the attack arrives through the prompt, not the permissions. So the real test is not "can the agent query the database." It is "what happens when the prompt tells it to do something it shouldn't."&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>🪄🐘 Postgres best practices I wish every app developer knew 🧑‍💻</title>
      <dc:creator>Adela</dc:creator>
      <pubDate>Sun, 14 Jun 2026 20:52:27 +0000</pubDate>
      <link>https://dev.to/bytebase/postgres-best-practices-i-wish-every-app-developer-knew-4f2d</link>
      <guid>https://dev.to/bytebase/postgres-best-practices-i-wish-every-app-developer-knew-4f2d</guid>
      <description>&lt;p&gt;At Bytebase we work with PostgreSQL every day, both inside our own product and across the databases our customers point us at. The same handful of mistakes shows up over and over, and almost none of them are exotic. They are small things an application developer controls and skips anyway.&lt;/p&gt;

&lt;p&gt;So this is not a database administration checklist. These are the five things I wish every app developer turned on by default. Each takes minutes, and each one buys you something concrete: a connection you can actually debug, a query that fails fast instead of hanging, a deploy that doesn't lock prod. Do them and your DBA stops paging you at 2am. There's a bonus index trick at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Set application_name for Better Debugging
&lt;/h2&gt;

&lt;p&gt;This is the cheapest win on the list, and the one people skip most. By default every connection from your fleet looks identical. When something melts down and you're staring at a wall of &lt;code&gt;pg_stat_activity&lt;/code&gt; rows, all the same user from the same pooler IP, you have no idea which service is the culprit. &lt;code&gt;application_name&lt;/code&gt; is a free label. Set it once per service and the noise turns into a name.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Implement
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL connection string:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgresql://user:password@localhost:5432/mydb?application_name=user-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Node.js with pg:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mydb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myuser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mypassword&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;application_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python with psycopg2:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mydb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;myuser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mypassword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;application_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Java with JDBC:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"jdbc:postgresql://localhost:5432/mydb?ApplicationName=user-service"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DriverManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"myuser"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"mypassword"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Go with lib/pq:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"database/sql"&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="s"&gt;"github.com/lib/pq"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;connStr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"host=localhost dbname=mydb user=myuser password=mypassword application_name=user-service"&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connStr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Get
&lt;/h3&gt;

&lt;p&gt;Now &lt;code&gt;pg_stat_activity&lt;/code&gt; actually tells you something:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- See all active connections from your service&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;application_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_activity&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;application_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user-service'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Monitor long-running queries from specific services&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;application_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;query_start&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_stat_activity&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;query_start&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'30 seconds'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Configure PostgreSQL Statement and Lock Timeouts
&lt;/h2&gt;

&lt;p&gt;Postgres will wait forever if you let it. With no statement timeout, one bad query sits there holding a connection until a human notices. Do that across a pool and you don't get a slow app, you get an outage: every slot taken, nothing left to serve real traffic. Four timeouts decide how patient Postgres is willing to be. Set all four.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Four Critical Timeouts
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;statement_timeout&lt;/code&gt;. The ceiling on any single statement. This is the one to set first; it kills runaway queries before they take the pool down with them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;lock_timeout&lt;/code&gt;. How long a statement waits for a lock before giving up, instead of blocking your whole request path behind someone else's transaction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;idle_in_transaction_timeout&lt;/code&gt;. Reaps a transaction that opened, grabbed locks, and then went quiet because the app forgot to commit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;transaction_timeout&lt;/code&gt;. The ceiling on an entire transaction, not just one statement (PostgreSQL 17+). Stops a long transaction from sitting on locks the whole time.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How to Configure
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;In connection string:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgresql://user:pass@localhost/db?options=-c%20statement_timeout=30s%20-c%20lock_timeout=10s%20-c%20transaction_timeout=60s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;At transaction level:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;statement_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'30s'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;lock_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'10s'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;transaction_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'60s'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- PostgreSQL 17+&lt;/span&gt;
&lt;span class="c1"&gt;-- Your transaction operations here&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Recommended Values by Use Case
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;statement_timeout&lt;/th&gt;
&lt;th&gt;lock_timeout&lt;/th&gt;
&lt;th&gt;idle_in_transaction_timeout&lt;/th&gt;
&lt;th&gt;transaction_timeout&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Web Applications&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;30s&lt;/td&gt;
&lt;td&gt;10s&lt;/td&gt;
&lt;td&gt;60s&lt;/td&gt;
&lt;td&gt;60s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Background Jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;300s+&lt;/td&gt;
&lt;td&gt;30s&lt;/td&gt;
&lt;td&gt;300s&lt;/td&gt;
&lt;td&gt;600s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reporting/Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0 (disabled)&lt;/td&gt;
&lt;td&gt;60s&lt;/td&gt;
&lt;td&gt;600s&lt;/td&gt;
&lt;td&gt;0 (disabled)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The logic is just patience matched to purpose. A user is staring at a spinner, so web requests should fail fast. Background jobs run unattended and can afford to wait a little longer for a lock. Analytics queries legitimately run for hours, so you disable the statement cap there and accept that those sessions are the exception, not the rule.&lt;/p&gt;



&lt;p&gt;To learn more, please refer &lt;a href="https://dev.to/blog/postgres-timeout"&gt;Postgres Timeout Explained&lt;/a&gt;.&lt;/p&gt;



&lt;h2&gt;
  
  
  3. Run Online Schema Migrations Without Downtime
&lt;/h2&gt;

&lt;p&gt;This is where a one-line migration becomes an incident. A statement that runs in 20ms on your laptop can lock a 50-million-row table for ten minutes in production, and for those ten minutes every read and write behind it queues up. The thing that actually takes prod down isn't the migration. It's the pile of normal traffic stuck waiting on its lock.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with Standard Migrations
&lt;/h3&gt;

&lt;p&gt;Traditional migrations acquire exclusive locks that block all reads and writes to your tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- DON'T: This blocks ALL SELECT, INSERT, UPDATE, DELETE on users table&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;email_verified&lt;/span&gt; &lt;span class="nb"&gt;boolean&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- DON'T: This blocks ALL writes (INSERT, UPDATE, DELETE) on orders table for hours&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a small table you'll never notice. On a large one, the same statement blocks every query for minutes, the connection pool drains as traffic backs up behind it, and the stall cascades into every service that touches that table. One &lt;code&gt;ALTER&lt;/code&gt; takes down half the stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Online Migration Strategies
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="c1"&gt;-- Safe: Builds index without blocking reads/writes&lt;/span&gt;
   &lt;span class="c1"&gt;-- Note: Cannot be run inside a transaction&lt;/span&gt;
   &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt; &lt;span class="n"&gt;idx_orders_user_created&lt;/span&gt;
   &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;To learn more production tips, please refer &lt;a href="https://dev.to/blog/postgres-create-index-concurrently"&gt;Postgres CREATE INDEX CONCURRENTLY&lt;/a&gt;.&lt;/p&gt;



&lt;ol&gt;
&lt;li&gt;Add columns and constraints safely
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="c1"&gt;-- Step 1: Add constraint without validation (fast)&lt;/span&gt;
   &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;email_verified_not_null&lt;/span&gt;
   &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email_verified&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;VALID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;-- Step 2: Validate constraint (can be done later, non-blocking)&lt;/span&gt;
   &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;VALIDATE&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;email_verified_not_null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;-- Step 3: Convert to NOT NULL constraint&lt;/span&gt;
   &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;email_verified&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Split PostgreSQL Roles for Different Operations
&lt;/h2&gt;

&lt;p&gt;Most apps connect to Postgres as one user that can do everything, because that's what the quickstart told you to do. It works right up until a bug in your code runs a &lt;code&gt;DROP TABLE&lt;/code&gt; it should never have been allowed to run. Least privilege isn't bureaucracy here. It's the difference between a bug that returns a permission error and a bug that drops prod by accident.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with Single "Super User" Approach
&lt;/h3&gt;

&lt;p&gt;Many applications use a single database user with broad permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- DON'T: Single user with excessive privileges&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You inherit four problems at once. The app carries DDL rights it never uses, so a single bug can reshape the schema. Every connection is the same identity, so in &lt;code&gt;pg_stat_activity&lt;/code&gt; you can't tell a runaway report apart from your write path. You can't tune connection settings per workload. And your audit trail says "the app did it", which tells you nothing. One identity, every risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Three-Role Strategy
&lt;/h3&gt;

&lt;p&gt;Three roles cover almost every app. Think three keys, not one master key:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read-Only Role - For analytics, reporting, read replicas&lt;/li&gt;
&lt;li&gt;Application Role - For normal CRUD operations&lt;/li&gt;
&lt;li&gt;Migration Role - For schema changes and DDL operations&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementation Example
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Create the roles:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Read-only role for analytics/reporting&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;app_reader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CONNECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_reader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_reader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_reader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Automatically grant SELECT on future tables&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_reader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Application role for normal operations&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CONNECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;SEQUENCES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Auto-grant on future objects&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;SEQUENCES&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Migration role for schema changes&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;app_migrator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;CONNECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_migrator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_migrator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_migrator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;SEQUENCES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_migrator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create actual users:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create users and assign roles&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;analytics_user&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'secure_password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;app_reader&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;analytics_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;application_user&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'secure_password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;app_writer&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;application_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;migration_user&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'secure_password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;app_migrator&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;migration_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Use UPSERT for Idempotent Writes
&lt;/h2&gt;

&lt;p&gt;Networks drop, clients retry, two requests race. In production this isn't an edge case, it's Tuesday. UPSERT (&lt;code&gt;INSERT ... ON CONFLICT&lt;/code&gt;) lets you write code that doesn't care whether the row already exists. A retry can't create a duplicate, a race resolves itself, and the "create or update" branching you'd otherwise write in the app disappears. The database settles the conflict atomically, which is exactly where you want that decision made.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignore Duplicates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Pattern 1: Ignore duplicates&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'john@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;NOTHING&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update on Conflict
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Pattern 2: Update on conflict&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'john@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'John Smith'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditional Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Pattern 3: Conditional update&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;user_stats&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;login_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="n"&gt;login_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login_count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;last_login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_login&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_login&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_login&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: Implement "Invisible" Indexes
&lt;/h2&gt;

&lt;p&gt;PostgreSQL doesn't ship invisible indexes the way MySQL 8 does. But you can fake it by flipping the &lt;code&gt;indisvalid&lt;/code&gt; flag in the &lt;code&gt;pg_index&lt;/code&gt; catalog, which takes an index out of the planner's consideration without dropping it. Handy when you suspect an index isn't earning its keep and want to confirm that before you actually remove it. Just know you're poking a system catalog directly, so keep it to staging or a maintenance window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making an Index "Invisible"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Disable an existing index (make it invisible to the query planner)&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;pg_index&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;indisvalid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;indexrelid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'idx_orders_status'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Making the Index "Visible" Again
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Re-enable the index (make it visible to the query planner)&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;pg_index&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;indisvalid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;indexrelid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'idx_orders_status'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Index Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- See which indexes are currently disabled&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;schemaname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;tablename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;indexname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;indisvalid&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;is_valid&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_indexes&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;pg_index&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;pg_indexes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indexname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relname&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;pg_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indexrelid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;indisvalid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: PostgreSQL itself uses the &lt;code&gt;indisvalid&lt;/code&gt; flag internally. When you run &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;, PostgreSQL automatically sets &lt;code&gt;indisvalid = false&lt;/code&gt; until the index is fully built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;None of this is database administration. It's five things an application developer controls and usually leaves on the floor: name your connections, set your timeouts, migrate online, split your roles, write idempotent inserts. Start with &lt;code&gt;application_name&lt;/code&gt; and timeouts. They take ten minutes and pay you back the first time something breaks at 2am.&lt;/p&gt;

&lt;p&gt;None of these will win you praise in a design review. They just quietly keep you out of the incident channel, which is the whole point.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
    </item>
    <item>
      <title>Supabase ⚡️ vs. Firebase 🔥: a Complete Comparison ⚔️ in 2025</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Thu, 07 Aug 2025 08:07:04 +0000</pubDate>
      <link>https://dev.to/bytebase/supabase-vs-firebase-a-complete-comparison-in-2025-1k80</link>
      <guid>https://dev.to/bytebase/supabase-vs-firebase-a-complete-comparison-in-2025-1k80</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Supabase and Firebase are two leading &lt;strong&gt;Backend-as-a-Service (BaaS) platforms&lt;/strong&gt; that enable developers to build applications without handling backend infrastructure. While they serve similar purposes, they take fundamentally different approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt; began in 2011 as a real-time NoSQL database and was acquired by Google in 2014. Since then, it has evolved into a comprehensive, fully-managed backend platform deeply integrated with the Google ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt;, launched in 2020, emerged as an &lt;strong&gt;open-source alternative&lt;/strong&gt; to Firebase. It’s built on &lt;strong&gt;PostgreSQL&lt;/strong&gt;, offering a relational model with SQL support, and can be self-hosted for greater control and transparency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase&lt;/th&gt;
&lt;th&gt;Firebase&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Philosophy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-source, standards-based&lt;/td&gt;
&lt;td&gt;Proprietary, fully-managed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL (Relational)&lt;/td&gt;
&lt;td&gt;Firestore (NoSQL) &amp;amp; Realtime Database (NoSQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Relational with tables, schemas, and SQL&lt;/td&gt;
&lt;td&gt;Document-based with collections and documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query Capabilities&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full SQL support with joins, complex queries&lt;/td&gt;
&lt;td&gt;Limited query options, no native joins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Email/password, social, phone, MFA&lt;/td&gt;
&lt;td&gt;Email/password, social, phone, MFA, anonymous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL logical replication&lt;/td&gt;
&lt;td&gt;Purpose-built real-time infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Offline Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic, still evolving&lt;/td&gt;
&lt;td&gt;Comprehensive, mature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Edge Functions (Deno)&lt;/td&gt;
&lt;td&gt;Cloud Functions (Node.js, Python, Go, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3-compatible with RLS&lt;/td&gt;
&lt;td&gt;Google Cloud Storage with Security Rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI/ML&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vector database for embeddings, OpenAI/Hugging Face integrations, RAG support&lt;/td&gt;
&lt;td&gt;Firebase Studio, Genkit, Gemini API integration, Vertex AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Predictable tiered pricing&lt;/td&gt;
&lt;td&gt;Usage-based, pay-as-you-go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self-hosting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Available&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data-intensive apps, SQL expertise, complex relationships&lt;/td&gt;
&lt;td&gt;Mobile apps, real-time features, rapid prototyping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Core Philosophy and Approach
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt; embraces open-source technologies and standards, with PostgreSQL at its core. It provides direct SQL access and follows relational database principles, emphasizing data portability and avoiding vendor lock-in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt; takes a proprietary, fully-managed approach focused on developer experience and seamless integration. It abstracts away infrastructure complexities with NoSQL databases optimized for real-time synchronization and mobile use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Capabilities
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt; leverages PostgreSQL's powerful relational capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong data consistency with full ACID compliance&lt;/li&gt;
&lt;li&gt;Support for complex queries, including joins and multi-step transactions&lt;/li&gt;
&lt;li&gt;Rich data types and advanced indexing for performance tuning&lt;/li&gt;
&lt;li&gt;Native support for stored procedures and triggers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt; offers two NoSQL database options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firestore:&lt;/strong&gt; A document-based database using collections and documents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Realtime Database:&lt;/strong&gt; A lightweight JSON tree structure optimized for real-time data synchronization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supabase excels in complex data relationships and advanced querying, while Firebase prioritizes real-time synchronization and automatic scaling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication and Security
&lt;/h2&gt;

&lt;p&gt;Both platforms offer comprehensive authentication with email/password, social logins, and multi-factor authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built on &lt;strong&gt;PostgreSQL Row Level Security (RLS)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Access control is enforced through &lt;strong&gt;SQL-based policies&lt;/strong&gt;, allowing fine-grained, table-level permissions.&lt;/li&gt;
&lt;li&gt;Offers flexibility and transparency, ideal for SQL-savvy teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;strong&gt;Firebase Security Rules&lt;/strong&gt;, written in a &lt;strong&gt;JavaScript-like syntax&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Rules are service-specific (e.g., Firestore, Storage), enabling dynamic access control based on user roles and request context.&lt;/li&gt;
&lt;li&gt;Tight integration with Google services makes setup straightforward, especially for front-end developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supabase provides &lt;strong&gt;database-native&lt;/strong&gt; security controls ideal for complex permission schemes, while Firebase offers &lt;strong&gt;service-specific&lt;/strong&gt; rules that are easier to implement for common scenarios. Supabase's SQL-based RLS gives more granular database control, whereas Firebase rules are more approachable for developers without SQL experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-time and Offline Capabilities
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt; offers battle-tested real-time features with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic data synchronization across clients&lt;/li&gt;
&lt;li&gt;Built-in offline support with local persistence&lt;/li&gt;
&lt;li&gt;Automatic conflict resolution and smooth reconnection handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Supabase Realtime&lt;/strong&gt; is powered by PostgreSQL logical replication and Phoenix Channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enables database change subscriptions, including INSERT, UPDATE, DELETE events&lt;/li&gt;
&lt;li&gt;Supports presence tracking for multiplayer or collaborative use cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Firebase leads in offline resilience and real-time maturity, while Supabase offers powerful server-driven change tracking with a relational foundation, best suited for applications needing strong data consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless Functions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Supabase Edge Functions&lt;/strong&gt; are built on the &lt;strong&gt;Deno runtime&lt;/strong&gt;, supporting &lt;strong&gt;TypeScript and JavaScript&lt;/strong&gt;, and are designed to run at the edge for low-latency performance. These functions have direct access to the Supabase PostgreSQL database, making them ideal for lightweight APIs and custom logic tightly coupled with the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firebase Cloud Functions&lt;/strong&gt; support multiple runtimes, including &lt;strong&gt;Node.js, Python, and Go&lt;/strong&gt;, and can be triggered by a wide range of events — such as HTTP requests, Firestore updates, authentication events, and Pub/Sub messages. They are deeply integrated with Google Cloud Platform for scalability and flexibility.&lt;/p&gt;

&lt;p&gt;Supabase Edge Functions are &lt;strong&gt;lightweight, TypeScript-native, and database-aware&lt;/strong&gt;, while Firebase Cloud Functions provide &lt;strong&gt;broad language support, rich trigger options, and tight integration&lt;/strong&gt; with the wider Google Cloud ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI and Machine Learning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt; offers a comprehensive, cloud-based AI development ecosystem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Studio&lt;/strong&gt;: Cloud development environment for AI apps with Gemini integration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Genkit&lt;/strong&gt;: Open-source framework for building AI features with generative models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertex AI Integration&lt;/strong&gt;: Direct access to Google's Gemini models for text, image, video, and audio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-assisted Development&lt;/strong&gt;: Gemini assistance for coding and app development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt; takes a database-centric approach to AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector Database (pgvector)&lt;/strong&gt;: PostgreSQL extension for vector similarity search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Toolkit&lt;/strong&gt;: Tools for storing, indexing, and querying vector embeddings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Search&lt;/strong&gt;: Built-in capabilities for meaning-based search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Integrations&lt;/strong&gt;: Support for OpenAI, Hugging Face, LangChain, and other providers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt; excels with its fully integrated AI development environment and seamless access to Google's advanced models. &lt;strong&gt;Supabase&lt;/strong&gt; shines by offering flexible, database-native AI capabilities built on PostgreSQL, ideal for developers who want tight control over their AI data infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing Models
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; (Predictable Tiered Pricing)&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Firebase&lt;/strong&gt; (Usage-Based, à la Carte Pricing)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free Tier&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generous limits&lt;/td&gt;
&lt;td&gt;Daily/monthly quotas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Paid Plans&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pro plan ($25/month), plus Team and Enterprise options&lt;/td&gt;
&lt;td&gt;Blaze plan (pay-per-operation), integrated with Google Cloud billing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost Structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fixed, predictable pricing&lt;/td&gt;
&lt;td&gt;Variable, pay-as-you-go per usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Better for growing teams needing cost predictability&lt;/td&gt;
&lt;td&gt;May be cheaper for small apps, but expensive as usage scales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analogy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fixed-price buffet: predictable and straightforward&lt;/td&gt;
&lt;td&gt;À la carte menu: flexible but potentially unpredictable costs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Performance and Reliability
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built on &lt;strong&gt;Google's global infrastructure&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic scaling&lt;/strong&gt; with minimal configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-region replication&lt;/strong&gt; ensures high availability and low latency&lt;/li&gt;
&lt;li&gt;Optimized for &lt;strong&gt;real-time data synchronization&lt;/strong&gt; and global distribution&lt;/li&gt;
&lt;li&gt;Ideal for apps needing instant updates and seamless scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosted on &lt;strong&gt;AWS&lt;/strong&gt; with support for multiple regions&lt;/li&gt;
&lt;li&gt;Offers &lt;strong&gt;read replicas&lt;/strong&gt; for scaling read-heavy workloads&lt;/li&gt;
&lt;li&gt;Requires &lt;strong&gt;manual performance tuning&lt;/strong&gt; for high-traffic or complex queries&lt;/li&gt;
&lt;li&gt;Capable of strong performance, especially for &lt;strong&gt;relational workloads&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More control and flexibility&lt;/strong&gt;, but with added configuration overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Firebase offers "set-and-forget" performance at global scale, while Supabase provides more control for database tuning at the cost of manual optimization. Firebase handles traffic spikes automatically, whereas Supabase requires planning for high-load scenarios but can achieve better PostgreSQL-specific performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Choose Supabase for SQL-powered apps needing PostgreSQL flexibility, open-source control, or AI vector support. Opt for Firebase if you prioritize real-time mobile apps, rapid prototyping, and Google's ecosystem. Supabase offers deeper database control; Firebase delivers effortless scaling and real-time sync.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>firebase</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Supabase vs AWS: Feature 🔖 and Pricing 💰 Comparison (2025)</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Tue, 29 Jul 2025 06:20:00 +0000</pubDate>
      <link>https://dev.to/bytebase/supabase-vs-aws-feature-and-pricing-comparison-2025-16id</link>
      <guid>https://dev.to/bytebase/supabase-vs-aws-feature-and-pricing-comparison-2025-16id</guid>
      <description>&lt;p&gt;When comparing &lt;strong&gt;Supabase&lt;/strong&gt; and &lt;strong&gt;AWS&lt;/strong&gt;, the key question isn't just about raw features, it's about how much functionality you get &lt;strong&gt;per dollar&lt;/strong&gt; and how easily you can scale from a free plan to enterprise-grade infrastructure.&lt;/p&gt;

&lt;p&gt;In this guide, we break down the pricing and functionality for each of &lt;strong&gt;Supabase’s core features&lt;/strong&gt; and compare them to their &lt;strong&gt;closest AWS counterparts&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supabase Database → AWS RDS PostgreSQL&lt;/li&gt;
&lt;li&gt;Supabase Auth → AWS Cognito&lt;/li&gt;
&lt;li&gt;Supabase Storage → AWS S3&lt;/li&gt;
&lt;li&gt;Supabase Edge Functions → AWS Lambda&lt;/li&gt;
&lt;li&gt;Supabase Realtime (Messages) → AWS SQS/SNS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll go &lt;strong&gt;feature by feature&lt;/strong&gt;, then compare &lt;strong&gt;total costs at different usage tiers&lt;/strong&gt;, and conclude with final recommendations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature by Feature
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🗄️ Database: Supabase DB vs AWS RDS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Supabase:&lt;/strong&gt; Built-in PostgreSQL with real-time, REST API, and simple pricing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS RDS/Aurora:&lt;/strong&gt; Fully managed PostgreSQL with high configurability, performance tuning, and multi-AZ support.&lt;/p&gt;

&lt;p&gt;🧬 Supabase is easier and more predictable. RDS gives more control and is better for compliance-heavy or complex workloads.&lt;/p&gt;

&lt;p&gt;See our full comparison here: &lt;a href="https://dev.to/blog/supabase-vs-aws-database-pricing/"&gt;Supabase vs AWS Database Pricing (2025)&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supabase bundles compute, storage, backup, and bandwidth into flat tiers.&lt;/li&gt;
&lt;li&gt;AWS RDS offers cheaper options if you commit to Reserved Instances, but requires piecing together compute, storage, backup, and bandwidth costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➡️ If you want more predictable cost and fast setup, choose Supabase. If you need performance tuning or compliance, go with RDS.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Auth: Supabase Auth vs AWS Cognito
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Supabase:&lt;/strong&gt; Easy-to-use auth system with social login, magic links, phone support, and DB integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Cognito:&lt;/strong&gt; Feature-rich identity management with SSO, federation, and enterprise IAM support.&lt;/p&gt;

&lt;p&gt;🧬 &lt;strong&gt;Supabase Auth is simpler and developer-friendly&lt;/strong&gt;, great for fast setup and apps with straightforward auth needs. &lt;strong&gt;Cognito is better for enterprises&lt;/strong&gt; that require SSO, security compliance, and deep AWS integration.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase Auth&lt;/th&gt;
&lt;th&gt;AWS Cognito&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free Tier&lt;/td&gt;
&lt;td&gt;50,000 MAUs&lt;/td&gt;
&lt;td&gt;50,000 MAUs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paid Pricing&lt;/td&gt;
&lt;td&gt;\$0.0015/MAU&lt;/td&gt;
&lt;td&gt;\$0.0055/MAU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSO Support&lt;/td&gt;
&lt;td&gt;Enterprise plan only&lt;/td&gt;
&lt;td&gt;Included (OIDC, SAML)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MFA / Passwords&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate Limits&lt;/td&gt;
&lt;td&gt;Generous&lt;/td&gt;
&lt;td&gt;Strict (especially SAML)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;➡️ Choose Supabase if you want fast, simple auth for your apps. However, as your business grows, your team will likely need a more sophisticated auth solution. Cognito is one option, but many teams also consider Okta, Azure Entra, or WorkOS.&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 Storage: Supabase Storage vs AWS S3
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Supabase:&lt;/strong&gt; S3-compatible storage with built-in access control, REST API, and CDN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS S3:&lt;/strong&gt; Scalable object store with advanced features and global durability.&lt;/p&gt;

&lt;p&gt;🧬 Supabase is easier to use for app developers. S3 is more powerful and flexible for enterprise-scale needs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase Storage&lt;/th&gt;
&lt;th&gt;AWS S3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free Tier&lt;/td&gt;
&lt;td&gt;1GB&lt;/td&gt;
&lt;td&gt;5GB (12 months only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paid Pricing&lt;/td&gt;
&lt;td&gt;\$0.021/GB&lt;/td&gt;
&lt;td&gt;\$0.023/GB (Standard)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Simple REST&lt;/td&gt;
&lt;td&gt;REST/S3 SDKs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions&lt;/td&gt;
&lt;td&gt;Built-in (row-level)&lt;/td&gt;
&lt;td&gt;IAM Policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Optional (CloudFront)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;➡️ It seems that Supabase Storage is built on top of AWS S3, providing an easy-to-use interface and lower pricing—likely due to volume discounts they can secure. The tradeoff is reduced flexibility in managing the underlying S3 buckets directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ Edge Functions: Supabase vs AWS Lambda
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Supabase:&lt;/strong&gt; Deno-based edge functions with tight database integration and minimal cold starts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Lambda:&lt;/strong&gt; Flexible, multi-runtime functions with deep AWS service integration.&lt;/p&gt;

&lt;p&gt;🧬 Supabase is simpler for edge APIs. Lambda is better for complex logic, language flexibility, and AWS ecosystem workflows.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase Edge Functions&lt;/th&gt;
&lt;th&gt;AWS Lambda&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free Tier&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500,000 invocations&lt;/td&gt;
&lt;td&gt;1 million requests/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deno&lt;/td&gt;
&lt;td&gt;Node.js, Python, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold Starts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Varies by region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Region&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Global edge&lt;/td&gt;
&lt;td&gt;Region-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Request Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;\$2 per million&lt;/strong&gt; (includes compute)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;\$0.20 per million&lt;/strong&gt; (requests only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compute Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Included in flat price&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Additional&lt;/strong&gt;: \$0.00001667 per GB-s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; charges a flat fee per call — &lt;strong&gt;simpler but may appear higher&lt;/strong&gt; at first glance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lambda&lt;/strong&gt; splits billing into two parts:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Requests&lt;/strong&gt; (cheap)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute&lt;/strong&gt; (based on memory x execution time), which can add up.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;➡️ Supabase Edge charges only an invocation fee, while AWS Lambda has separate charges for invocations and compute time, measured in GB-seconds. Additionally, AWS Lambda supports a wider range of runtimes.&lt;/p&gt;

&lt;h3&gt;
  
  
  📡 Realtime: Supabase Messages vs AWS SQS/SNS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Supabase:&lt;/strong&gt; WebSocket-based realtime updates triggered by PostgreSQL changes. Great for simple in-app messaging and live UIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS SQS/SNS:&lt;/strong&gt; Scalable, fully managed messaging services. Ideal for decoupled, event-driven architectures and backend pipelines.&lt;/p&gt;

&lt;p&gt;🧬 Supabase is great for realtime UX. AWS is better for complex, distributed systems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase Realtime&lt;/th&gt;
&lt;th&gt;AWS SQS / SNS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Free Tier&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500,000 messages/month&lt;/td&gt;
&lt;td&gt;1 million requests/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pub/Sub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;SNS only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queueing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;SQS provides advanced queueing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL triggers&lt;/td&gt;
&lt;td&gt;Broad: Lambda, S3, EC2, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;\$2 per million messages&lt;/strong&gt; (flat)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;\$0.40–\$0.50 per million requests&lt;/strong&gt; + delivery &amp;amp; data transfer costs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; charges a flat fee per message, making pricing simple and predictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS SQS/SNS&lt;/strong&gt; charges separately for requests, message delivery, and data transfer, offering greater flexibility and scalability for complex event-driven systems.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQS Standard&lt;/strong&gt;: ~\$0.40 per million requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS&lt;/strong&gt;: ~\$0.50 per million publishes&lt;/li&gt;
&lt;li&gt;Additional charges: Message delivery (e.g., to Lambda, HTTP endpoints, or SQS) and &lt;strong&gt;Data transfer&lt;/strong&gt; if messages cross regions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;➡️ &lt;strong&gt;Supabase Realtime charges a flat fee per message&lt;/strong&gt;, making pricing simple and predictable. &lt;strong&gt;AWS SQS/SNS charges separately for requests, message delivery, and data transfer&lt;/strong&gt;, offering greater flexibility and scalability for complex event-driven systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  💰 Pricing Comparison by Tier
&lt;/h2&gt;

&lt;h3&gt;
  
  
  💡 About AWS Reserved Pricing
&lt;/h3&gt;

&lt;p&gt;The AWS prices shown below are based on &lt;strong&gt;on-demand usage&lt;/strong&gt;, which is flexible but more expensive.&lt;br&gt;
If you commit to &lt;strong&gt;1-year or 3-year Reserved Instances&lt;/strong&gt; (RIs), you can cut costs by &lt;strong&gt;30–70%&lt;/strong&gt; — especially for RDS and Lambda.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RDS db.m5.large (Multi-AZ)&lt;/strong&gt; drops from &lt;strong&gt;\$250+/mo&lt;/strong&gt; to &lt;strong&gt;~\$145/mo&lt;/strong&gt; (1-year RI, no upfront)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda and compute&lt;/strong&gt; costs can be reduced via &lt;strong&gt;Compute Savings Plans&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Use on-demand for flexibility.&lt;br&gt;
🔐 Use Reserved pricing for long-term, cost-optimized workloads — but it comes with lock-in.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 0. Free Tier
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase&lt;/th&gt;
&lt;th&gt;AWS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;✅ Shared CPU /500MB PostgreSQL&lt;/td&gt;
&lt;td&gt;⚠️ 2 vCPU (burstable) / 1GB RAM for t4g.micro (12 months)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;✅ 50K MAUs&lt;/td&gt;
&lt;td&gt;✅ 50K MAUs (Cognito)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;✅ 1GB w/ CDN&lt;/td&gt;
&lt;td&gt;⚠️ 5GB (12 months, no CDN)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Functions&lt;/td&gt;
&lt;td&gt;✅ 500K Edge calls&lt;/td&gt;
&lt;td&gt;✅ 1M Lambda calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messaging&lt;/td&gt;
&lt;td&gt;✅ 500K Realtime messages&lt;/td&gt;
&lt;td&gt;✅ 1M SQS/SNS messages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;🔍 &lt;strong&gt;Supabase offers a complete full-stack platform for free, with no expiration.&lt;/strong&gt;&lt;br&gt;
AWS has generous limits — but database and storage expire after 12 months.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 1. Startup (10K MAUs, 20GB DB, 50GB Storage, 500GB bandwidth)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Supabase – &lt;strong&gt;\$26.50/month&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Included Service&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pro Plan&lt;/td&gt;
&lt;td&gt;8GB DB, 100GB storage, 250GB bandwidth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB Storage Overage&lt;/td&gt;
&lt;td&gt;12GB extra @ \$0.125/GB = \$1.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth, Functions, Messaging&lt;/td&gt;
&lt;td&gt;Included in plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth&lt;/td&gt;
&lt;td&gt;Still within free 250GB – no charge&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;✅ &lt;strong&gt;Flat, all-inclusive plan with tiny storage add-on.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;On-Demand – &lt;strong&gt;\$75.00/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1-Year RI Estimate: &lt;strong&gt;~\$67.73/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;3-Year RI Estimate: &lt;strong&gt;~\$59.47/month&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Cost (On-Demand)&lt;/th&gt;
&lt;th&gt;1-Year RI (est. -30%)&lt;/th&gt;
&lt;th&gt;3-Year RI (est. -60%)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RDS (db.t3.small)&lt;/td&gt;
&lt;td&gt;\$24.82&lt;/td&gt;
&lt;td&gt;\$17.37&lt;/td&gt;
&lt;td&gt;\$9.93&lt;/td&gt;
&lt;td&gt;1 vCPU, 2GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS Storage (20GB)&lt;/td&gt;
&lt;td&gt;\$2.30&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;General-purpose SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito (10K MAUs)&lt;/td&gt;
&lt;td&gt;\$0.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Within free tier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 (50GB)&lt;/td&gt;
&lt;td&gt;\$1.15&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.023/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (500GB)&lt;/td&gt;
&lt;td&gt;\$45.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.09/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda (1M execs)&lt;/td&gt;
&lt;td&gt;\$2.73&lt;/td&gt;
&lt;td&gt;\$1.91&lt;/td&gt;
&lt;td&gt;\$1.09&lt;/td&gt;
&lt;td&gt;512MB, 200ms duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS (1M requests)&lt;/td&gt;
&lt;td&gt;\$0.50&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;SNS publish cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Most cost comes from bandwidth.&lt;/strong&gt; Setup involves configuring 5+ services.&lt;/p&gt;

&lt;h3&gt;
  
  
  📈 2. Growing Business (100K MAUs, 200GB DB, 1TB Storage, 5TB bandwidth)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Supabase – &lt;strong&gt;\$630.40/month&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pro Plan Base&lt;/td&gt;
&lt;td&gt;\$25.00&lt;/td&gt;
&lt;td&gt;Includes basic DB, auth, 100GB storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra DB Storage (192GB)&lt;/td&gt;
&lt;td&gt;\$24.00&lt;/td&gt;
&lt;td&gt;\$0.125/GB beyond included 8GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Upgrade&lt;/td&gt;
&lt;td&gt;\$60.00&lt;/td&gt;
&lt;td&gt;4-core, 2GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra Storage (900GB)&lt;/td&gt;
&lt;td&gt;\$18.90&lt;/td&gt;
&lt;td&gt;\$0.021/GB beyond included 100GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (4.75TB)&lt;/td&gt;
&lt;td&gt;\$427.50&lt;/td&gt;
&lt;td&gt;\$0.09/GB beyond 250GB included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edge Functions&lt;/td&gt;
&lt;td&gt;\$50.00&lt;/td&gt;
&lt;td&gt;Estimate for high usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messaging&lt;/td&gt;
&lt;td&gt;\$25.00&lt;/td&gt;
&lt;td&gt;Estimate for 10M events&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;✅ &lt;strong&gt;Integrated platform; all services billed under one umbrella.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;On-Demand – &lt;strong&gt;\$2,325.59/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1-Year RI Estimate: &lt;strong&gt;~\$2,000.69/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;3-Year RI Estimate: &lt;strong&gt;~\$1,675.79/month&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Cost (On-Demand)&lt;/th&gt;
&lt;th&gt;1-Year RI (est. -30%)&lt;/th&gt;
&lt;th&gt;3-Year RI (est. -60%)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RDS (db.m5.large Multi-AZ)&lt;/td&gt;
&lt;td&gt;\$249.66&lt;/td&gt;
&lt;td&gt;\$174.76&lt;/td&gt;
&lt;td&gt;\$99.86&lt;/td&gt;
&lt;td&gt;2 vCPU, 8GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS Storage (200GB)&lt;/td&gt;
&lt;td&gt;\$46.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;SSD, replicated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito (90K MAUs)&lt;/td&gt;
&lt;td&gt;\$495.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.0055/MAU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda (50M execs)&lt;/td&gt;
&lt;td&gt;\$833.33&lt;/td&gt;
&lt;td&gt;\$583.33&lt;/td&gt;
&lt;td&gt;\$333.33&lt;/td&gt;
&lt;td&gt;1GB, 500ms execs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 (1TB)&lt;/td&gt;
&lt;td&gt;\$23.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.023/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (5TB)&lt;/td&gt;
&lt;td&gt;\$450.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Tiered pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;\$175.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;50M HTTP requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch&lt;/td&gt;
&lt;td&gt;\$50.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS (10M requests)&lt;/td&gt;
&lt;td&gt;\$3.60&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.40 per million&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ &lt;strong&gt;More expensive due to metered pricing across services. Complex to manage.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🏢 3. Enterprise (1.5M MAUs, 2TB DB, 50TB Storage, 100TB bandwidth)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Supabase – &lt;strong&gt;\$19,383.40/month&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Team Plan&lt;/td&gt;
&lt;td&gt;\$599.00&lt;/td&gt;
&lt;td&gt;Enterprise features + support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB Storage (2TB)&lt;/td&gt;
&lt;td&gt;\$249.00&lt;/td&gt;
&lt;td&gt;Extra beyond base plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute (4XL)&lt;/td&gt;
&lt;td&gt;\$960.00&lt;/td&gt;
&lt;td&gt;16-core, 32GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth (1.5M MAUs)&lt;/td&gt;
&lt;td&gt;\$4,550.00&lt;/td&gt;
&lt;td&gt;\$0.00325 per MAU beyond 100K included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (50TB)&lt;/td&gt;
&lt;td&gt;\$1,047.90&lt;/td&gt;
&lt;td&gt;\$0.021/GB beyond 100GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (100TB)&lt;/td&gt;
&lt;td&gt;\$8,977.50&lt;/td&gt;
&lt;td&gt;\$0.09/GB beyond 250GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edge Functions&lt;/td&gt;
&lt;td&gt;\$2,000.00&lt;/td&gt;
&lt;td&gt;Custom pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messaging&lt;/td&gt;
&lt;td&gt;\$1,000.00&lt;/td&gt;
&lt;td&gt;Custom pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;✅ &lt;strong&gt;Predictable, transparent billing; all services built-in.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;On-Demand – &lt;strong&gt;\$73,122.81/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1-Year RI Estimate: &lt;strong&gt;~\$62,330.91/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;3-Year RI Estimate: &lt;strong&gt;~\$51,539.00/month&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Cost (On-Demand)&lt;/th&gt;
&lt;th&gt;1-Year RI (est. -30%)&lt;/th&gt;
&lt;th&gt;3-Year RI (est. -60%)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RDS (r5.4xlarge Multi-AZ)&lt;/td&gt;
&lt;td&gt;\$2,639.68&lt;/td&gt;
&lt;td&gt;\$1,847.78&lt;/td&gt;
&lt;td&gt;\$1,055.87&lt;/td&gt;
&lt;td&gt;16 vCPU, 128GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS Storage (2TB IOPS)&lt;/td&gt;
&lt;td&gt;\$2,500.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;High-performance SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito (1.5M MAUs)&lt;/td&gt;
&lt;td&gt;\$22,350.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.015 per MAU (Essential plan)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda (1B execs)&lt;/td&gt;
&lt;td&gt;\$33,333.33&lt;/td&gt;
&lt;td&gt;\$23,333.33&lt;/td&gt;
&lt;td&gt;\$13,333.33&lt;/td&gt;
&lt;td&gt;2GB RAM, 1s duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 (50TB)&lt;/td&gt;
&lt;td&gt;\$1,100.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Tiered pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (100TB)&lt;/td&gt;
&lt;td&gt;\$7,000.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Tiered egress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;\$3,500.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;1B HTTP requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch&lt;/td&gt;
&lt;td&gt;\$500.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Logging + monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS (500M messages)&lt;/td&gt;
&lt;td&gt;\$199.80&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.40 per million messages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Enterprise-grade everything — but costs are 3–4x higher.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 4. Hyperscale (10M MAUs, 10TB DB, 200TB Storage, 500TB Bandwidth)
&lt;/h3&gt;

&lt;p&gt;At this scale, you're operating at a level that most platforms — including Supabase — aren't built to serve out of the box.&lt;/p&gt;

&lt;h4&gt;
  
  
  Supabase – ❌ Not Recommended at This Scale
&lt;/h4&gt;

&lt;p&gt;Supabase’s architecture is optimized for small to mid-sized applications. While enterprise plans exist, the platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does not offer &lt;strong&gt;horizontal scalability&lt;/strong&gt; for PostgreSQL (no native sharding or clustering).&lt;/li&gt;
&lt;li&gt;Becomes &lt;strong&gt;cost-prohibitive&lt;/strong&gt; with 10M+ MAUs due to flat per-user pricing.&lt;/li&gt;
&lt;li&gt;Lacks fine-grained controls over &lt;strong&gt;networking, observability, and infrastructure&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;May require &lt;strong&gt;custom support contracts&lt;/strong&gt; and offloading core services (e.g., auth to WorkOS, storage to S3) to be viable.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Supabase is a great tool for rapid product development, but &lt;strong&gt;most teams at this scale migrate to cloud-native stacks&lt;/strong&gt; like AWS, GCP, or Azure for performance, reliability, and cost control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ If you're approaching hyperscale, you can still &lt;strong&gt;start with Supabase&lt;/strong&gt; — but architect with portability in mind.&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS – &lt;strong&gt;\$246,392.81/month (on-demand)&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;On-Demand&lt;/th&gt;
&lt;th&gt;1-Year RI (est. -30%)&lt;/th&gt;
&lt;th&gt;3-Year RI (est. -60%)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RDS (r6i.8xlarge Multi-AZ)&lt;/td&gt;
&lt;td&gt;\$10,560.00&lt;/td&gt;
&lt;td&gt;\$7,392.00&lt;/td&gt;
&lt;td&gt;\$4,224.00&lt;/td&gt;
&lt;td&gt;32 vCPU, 256GB RAM x2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS Storage (10TB IOPS)&lt;/td&gt;
&lt;td&gt;\$12,500.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;High IOPS SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito (9.9M MAUs)&lt;/td&gt;
&lt;td&gt;\$148,500.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.015 per MAU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda (10B execs)&lt;/td&gt;
&lt;td&gt;\$33,333.33&lt;/td&gt;
&lt;td&gt;\$23,333.33&lt;/td&gt;
&lt;td&gt;\$13,333.33&lt;/td&gt;
&lt;td&gt;2GB, 1s exec duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 (200TB)&lt;/td&gt;
&lt;td&gt;\$4,000.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Tiered pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (500TB)&lt;/td&gt;
&lt;td&gt;\$35,000.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Tiered egress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway (10B reqs)&lt;/td&gt;
&lt;td&gt;\$6,000.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$3.50/million HTTP requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch&lt;/td&gt;
&lt;td&gt;\$1,000.00&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Extended logs and metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS (1B messages)&lt;/td&gt;
&lt;td&gt;\$499.48&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;\$0.40 per million messages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;✅ &lt;strong&gt;AWS is built for hyperscale&lt;/strong&gt;: global infra, granular optimization, and predictable cost control through Reserved Instances and architectural tuning.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Supabase is a fantastic platform for startups and growing teams&lt;/strong&gt; — offering a unified experience, generous free tier, and simple pricing across auth, database, storage, edge functions, and realtime.&lt;/p&gt;

&lt;p&gt;It’s ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prototypes and MVPs&lt;/li&gt;
&lt;li&gt;Small-to-mid-scale production apps&lt;/li&gt;
&lt;li&gt;Teams that want to move fast without managing infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But as your product reaches &lt;strong&gt;hyperscale — millions of users, terabytes of data, and high-throughput compute&lt;/strong&gt; — &lt;strong&gt;Supabase starts to hit architectural and economic limits&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL isn’t horizontally scalable in their setup&lt;/li&gt;
&lt;li&gt;Auth and bandwidth costs grow steeply&lt;/li&gt;
&lt;li&gt;Fine-grained performance tuning and compliance become difficult&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛣️ Scale Path
&lt;/h3&gt;

&lt;p&gt;➡️ Many teams &lt;strong&gt;start with Supabase&lt;/strong&gt; to move quickly, then gradually &lt;strong&gt;offload services&lt;/strong&gt; (auth, compute, storage) to cloud-native infrastructure like &lt;strong&gt;AWS, GCP, or Azure&lt;/strong&gt; as scale and complexity grow.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Choose Supabase&lt;/strong&gt; if you want a fast, simple way to build and scale to your first million users.&lt;br&gt;
✅ &lt;strong&gt;Choose AWS&lt;/strong&gt; if you need performance at scale, deep customization, or global enterprise-grade infrastructure.&lt;/p&gt;

&lt;p&gt;👉 For a deep dive into database-specific pricing, see: &lt;a href="https://www.bytebase.com/blog/supabase-vs-aws-database-pricing/" rel="noopener noreferrer"&gt;Supabase vs AWS Database Pricing&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>supabase</category>
      <category>database</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Manage Databases 🛢️ with Terraform</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Tue, 22 Jul 2025 07:18:03 +0000</pubDate>
      <link>https://dev.to/bytebase/manage-databases-with-terraform-104o</link>
      <guid>https://dev.to/bytebase/manage-databases-with-terraform-104o</guid>
      <description>&lt;p&gt;This tutorial is part of the &lt;strong&gt;Bytebase Terraform Provider&lt;/strong&gt; series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://docs.bytebase.com/tutorials/manage-environments-with-terraform" rel="noopener noreferrer"&gt;Manage Environments with Terraform&lt;/a&gt; - Set up environments with policies&lt;/li&gt;
&lt;li&gt;Part 2: Manage Databases with Terraform 👈&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://docs.bytebase.com/tutorials/manage-projects-with-terraform" rel="noopener noreferrer"&gt;Manage Projects with Terraform&lt;/a&gt; - Organize databases into projects&lt;/li&gt;
&lt;li&gt;Part 4: &lt;a href="https://docs.bytebase.com/tutorials/manage-general-settings-with-terraform" rel="noopener noreferrer"&gt;Manage Bytebase Settings with Terraform&lt;/a&gt; - Configure workspace profile and approval policies&lt;/li&gt;
&lt;li&gt;Part 5: &lt;a href="https://docs.bytebase.com/tutorials/manage-sql-review-rules-with-terraform" rel="noopener noreferrer"&gt;Manage SQL Review Rules with Terraform&lt;/a&gt; - Define SQL review policies&lt;/li&gt;
&lt;li&gt;Part 6: &lt;a href="https://docs.bytebase.com/tutorials/manage-users-and-groups-with-terraform" rel="noopener noreferrer"&gt;Manage Users and Groups with Terraform&lt;/a&gt; - Configure users and groups&lt;/li&gt;
&lt;li&gt;Part 7: &lt;a href="https://docs.bytebase.com/tutorials/manage-database-access-control-with-terraform" rel="noopener noreferrer"&gt;Manage Database Access Control with Terraform&lt;/a&gt; - Grant database permissions&lt;/li&gt;
&lt;li&gt;Part 8: &lt;a href="https://docs.bytebase.com/tutorials/manage-data-masking-with-terraform" rel="noopener noreferrer"&gt;Manage Data Masking with Terraform&lt;/a&gt; - Protect sensitive data&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This tutorial series uses separate Terraform files for better organization. Files are numbered bytutorial part and sub-step (e.g., &lt;a href="https://github.com/bytebase/terraform-provider-bytebase/blob/main/tutorials/1-1-env-setting.tf" rel="noopener noreferrer"&gt;1-1-env-setting.tf&lt;/a&gt;, &lt;a href="https://github.com/bytebase/terraform-provider-bytebase/blob/main/tutorials/1-2-env-policy-rollout.tf" rel="noopener noreferrer"&gt;1-2-env-policy-rollout.tf&lt;/a&gt; for Part 1, &lt;a href="https://github.com/bytebase/terraform-provider-bytebase/blob/main/tutorials/2-instances.tf" rel="noopener noreferrer"&gt;2-instances.ti&lt;/a&gt; for Part 2, etc.). Terraform automatically handles dependencies between files.&lt;/p&gt;

&lt;p&gt;View sample code&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;p&gt;Register database instances in Bytebase using Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting this tutorial, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Completed &lt;a href="https://docs.bytebase.com/tutorials/manage-environments-with-terraform" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Bytebase running with built-in sample data&lt;/li&gt;
&lt;li&gt;Service account created&lt;/li&gt;
&lt;li&gt;Terraform initialized&lt;/li&gt;
&lt;li&gt;Environments configured&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1 - Explore Built-in Instances
&lt;/h2&gt;

&lt;p&gt;Bytebase sample data includes two sample PostgreSQL instances. Let's explore them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Bytebase, click &lt;strong&gt;Instances&lt;/strong&gt; on the left sidebar. You'll see two PostgreSQL instances:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test Sample Instance&lt;/strong&gt; (port 8083)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prod Sample Instance&lt;/strong&gt; (port 8084)&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;+Add Instance&lt;/strong&gt;, you may add more database instances.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewi1d28u30o0dy18hzoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewi1d28u30o0dy18hzoi.png" alt=" " width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Projects&lt;/strong&gt; on the left sidebar to see the &lt;code&gt;Sample Project&lt;/code&gt;, then click it. You'll see:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hr_test&lt;/code&gt; database on Test instance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hr_prod&lt;/code&gt; database on Prod instance&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;+ New DB&lt;/strong&gt;, you may create more databases on the existing instances.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzoq1tquq11p1qyqgxej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzoq1tquq11p1qyqgxej.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 - Register Instances with Terraform
&lt;/h2&gt;

&lt;p&gt;Now let's import these instances into Terraform management.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terraform resource&lt;/td&gt;
&lt;td&gt;&lt;a href="https://registry.terraform.io/providers/bytebase/bytebase/latest/docs/resources/instance" rel="noopener noreferrer"&gt;bytebase_instance&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sample file&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/bytebase/terraform-provider-bytebase/blob/main/tutorials/2-instances.tf" rel="noopener noreferrer"&gt;2-instances.tf&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Create &lt;code&gt;2-instances.tf&lt;/code&gt; with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```hcl 2-instances.tf&lt;/p&gt;

&lt;h1&gt;
  
  
  Test Sample Instance - PostgreSQL on port 8083
&lt;/h1&gt;

&lt;p&gt;resource "bytebase_instance" "test" {&lt;br&gt;
  depends_on  = [bytebase_setting.environments]&lt;br&gt;
  resource_id = "test-sample-instance"&lt;br&gt;
  environment = "environments/test"&lt;br&gt;
  title       = "Test Sample Instance"&lt;br&gt;
  engine      = "POSTGRES"&lt;br&gt;
  # Assign instance license&lt;br&gt;
  activation  = true&lt;/p&gt;

&lt;p&gt;# Connection settings for the built-in test database&lt;br&gt;
  data_sources {&lt;br&gt;
    id       = "admin-test"&lt;br&gt;
    type     = "ADMIN"&lt;br&gt;
    host     = "/tmp"      # Unix socket for local connection&lt;br&gt;
    port     = "8083"&lt;br&gt;
    username = "bbsample"&lt;br&gt;
    password = ""          # Empty for local auth&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  Production Sample Instance - PostgreSQL on port 8084
&lt;/h1&gt;

&lt;p&gt;resource "bytebase_instance" "prod" {&lt;br&gt;
  depends_on  = [bytebase_setting.environments]&lt;br&gt;
  resource_id = "prod-sample-instance"&lt;br&gt;
  environment = "environments/prod"&lt;br&gt;
  title       = "Prod Sample Instance"&lt;br&gt;
  engine      = "POSTGRES"&lt;br&gt;
  activation  = true&lt;/p&gt;

&lt;p&gt;data_sources {&lt;br&gt;
    id       = "admin-prod"&lt;br&gt;
    type     = "ADMIN"&lt;br&gt;
    host     = "/tmp"&lt;br&gt;
    port     = "8084"&lt;br&gt;
    username = "bbsample"&lt;br&gt;
    password = ""&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


**Understanding the Configuration**

- **depends_on**: Ensures environments from Part 1 exist first
- **resource_id**: Must match the existing instance ID
- **environment**: Links instance to Test or Prod environment
- **data_sources**: Connection details for the database

## Step 3 - Apply Configuration

Now apply the configuration:



```bash
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 4 - Verify Setup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Instances&lt;/strong&gt; in Bytebase.&lt;/li&gt;
&lt;li&gt;Verify both instances show:

&lt;ul&gt;
&lt;li&gt;Correct environment assignment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Y&lt;/code&gt; under License column (if using Enterprise)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can now modify instance properties through Terraform. For example, to rename an instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"bytebase_instance"&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# ... other config ...&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development Database"&lt;/span&gt;  &lt;span class="c1"&gt;# Changed title&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.bytebase.com/tutorials/manage-projects-with-terraform" rel="noopener noreferrer"&gt;Further Reading: Manage Projects with Terraform&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>database</category>
      <category>tooling</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Supabase vs AWS: Database Pricing 🛢️💰 Comparison in 2025</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Tue, 22 Jul 2025 03:59:21 +0000</pubDate>
      <link>https://dev.to/bytebase/supabase-vs-aws-database-pricing-comparison-in-2025-3ah3</link>
      <guid>https://dev.to/bytebase/supabase-vs-aws-database-pricing-comparison-in-2025-3ah3</guid>
      <description>&lt;p&gt;When choosing a PostgreSQL database platform, whether for a side project or a production app, you need to look beyond features and assess the &lt;strong&gt;total cost of ownership&lt;/strong&gt; — including compute, storage, backups, and bandwidth.&lt;/p&gt;

&lt;p&gt;In this guide, we compare &lt;strong&gt;Supabase vs AWS (RDS and Aurora)&lt;/strong&gt; across &lt;strong&gt;free, entry-level, and production tiers&lt;/strong&gt;. We focus strictly on database-related costs and explain &lt;strong&gt;on-demand vs reserved pricing&lt;/strong&gt; where applicable.&lt;/p&gt;

&lt;h2&gt;
  
  
  0. Free Plans: What Do You Get for \$0?
&lt;/h2&gt;

&lt;p&gt;Both platforms offer free tiers, but they differ significantly in &lt;strong&gt;duration&lt;/strong&gt;, &lt;strong&gt;compute power&lt;/strong&gt;, and &lt;strong&gt;resource isolation&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase Free Tier&lt;/th&gt;
&lt;th&gt;AWS Free Tier (12 months)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Duration&lt;/td&gt;
&lt;td&gt;Forever&lt;/td&gt;
&lt;td&gt;12 months from signup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;Shared CPU / 500MB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU (burstable) / 1GB RAM (t4g.micro, 750 hrs/mo)*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Type&lt;/td&gt;
&lt;td&gt;Shared container environment&lt;/td&gt;
&lt;td&gt;Dedicated EC2 instance (Graviton2, burstable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU Architecture&lt;/td&gt;
&lt;td&gt;x86&lt;/td&gt;
&lt;td&gt;ARM (AWS Graviton2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (DB)&lt;/td&gt;
&lt;td&gt;500MB&lt;/td&gt;
&lt;td&gt;20GB gp2*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup&lt;/td&gt;
&lt;td&gt;7-day snapshot&lt;/td&gt;
&lt;td&gt;20GB snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth&lt;/td&gt;
&lt;td&gt;5GB outbound&lt;/td&gt;
&lt;td&gt;15GB outbound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS / Isolation&lt;/td&gt;
&lt;td&gt;Serverless (no OS access)&lt;/td&gt;
&lt;td&gt;Full OS-level isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase Free&lt;/strong&gt; is ideal for hobby projects, quick MVPs, or internal tools with light traffic. It requires no setup and stays free forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Free Tier&lt;/strong&gt; gives you significantly more power and isolation but is limited to the first 12 months after signup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Entry-Level (Low Cost)
&lt;/h2&gt;

&lt;p&gt;When you outgrow the free tier but don’t need production-grade resources, these plans offer low-cost paths. Supabase simplifies everything with a flat rate. AWS offers more control and deeper savings through reserved pricing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase Pro Tier&lt;/th&gt;
&lt;th&gt;RDS t4g.micro (On-Demand)&lt;/th&gt;
&lt;th&gt;RDS t4g.micro (1yr Reserved)&lt;/th&gt;
&lt;th&gt;RDS t4g.micro (3yr Reserved)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly Price&lt;/td&gt;
&lt;td&gt;\$25 (includes \$10 compute)*&lt;/td&gt;
&lt;td&gt;\$11.68&lt;/td&gt;
&lt;td&gt;\$6.69&lt;/td&gt;
&lt;td&gt;\$4.76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;1 vCPU (shared) / 1GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU (burstable) / 1GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU (burstable) / 1GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU (burstable) / 1GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Type&lt;/td&gt;
&lt;td&gt;Shared container environment&lt;/td&gt;
&lt;td&gt;Dedicated ARM instance (Graviton2)&lt;/td&gt;
&lt;td&gt;Dedicated ARM instance (Graviton2)&lt;/td&gt;
&lt;td&gt;Dedicated ARM instance (Graviton2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS Access&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage Included&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;20GB gp2&lt;/td&gt;
&lt;td&gt;20GB gp2&lt;/td&gt;
&lt;td&gt;20GB gp2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra Storage&lt;/td&gt;
&lt;td&gt;\$0.125/GB&lt;/td&gt;
&lt;td&gt;\$0.115/GB&lt;/td&gt;
&lt;td&gt;\$0.115/GB&lt;/td&gt;
&lt;td&gt;\$0.115/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backups&lt;/td&gt;
&lt;td&gt;7-day snapshot&lt;/td&gt;
&lt;td&gt;Free up to DB size, then \$0.095/GB*&lt;/td&gt;
&lt;td&gt;Free up to DB size, then \$0.095/GB&lt;/td&gt;
&lt;td&gt;Free up to DB size, then \$0.095/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth&lt;/td&gt;
&lt;td&gt;250GB outbound included, then $0.09/GB&lt;/td&gt;
&lt;td&gt;\$0.09/GB outbound&lt;/td&gt;
&lt;td&gt;\$0.09/GB outbound&lt;/td&gt;
&lt;td&gt;\$0.09/GB outbound&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase Pro&lt;/strong&gt; is great if you want simple, predictable pricing without dealing with EC2, storage classes, or IOPS tuning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS On-Demand&lt;/strong&gt; offers low-cost dedicated compute with more configurability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reserved instances&lt;/strong&gt; (1yr and 3yr) reduce costs dramatically, but require long-term commitment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Mid-Tier Production (100GB Storage + Moderate Usage)
&lt;/h2&gt;

&lt;p&gt;For established applications with real user traffic and non-trivial data volumes, cost differences and platform flexibility become more significant.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase (Large)&lt;/th&gt;
&lt;th&gt;RDS m5.large (On-Demand)&lt;/th&gt;
&lt;th&gt;RDS m5.large (1yr Reserved)&lt;/th&gt;
&lt;th&gt;RDS m5.large (3yr Reserved)&lt;/th&gt;
&lt;th&gt;Aurora r5.large (On-Demand)&lt;/th&gt;
&lt;th&gt;Aurora r5.large (1yr Reserved)&lt;/th&gt;
&lt;th&gt;Aurora r5.large (3yr Reserved)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly Price&lt;/td&gt;
&lt;td&gt;\$110 (flat)*&lt;/td&gt;
&lt;td&gt;\$130&lt;/td&gt;
&lt;td&gt;\$81&lt;/td&gt;
&lt;td&gt;\$56&lt;/td&gt;
&lt;td&gt;\$211&lt;/td&gt;
&lt;td&gt;\$138&lt;/td&gt;
&lt;td&gt;\$96&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;2 vCPU (shared) / 8GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU / 8GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU / 8GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU / 8GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU / 16GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU / 16GB RAM&lt;/td&gt;
&lt;td&gt;2 vCPU / 16GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Type&lt;/td&gt;
&lt;td&gt;Shared container environment&lt;/td&gt;
&lt;td&gt;Dedicated EC2 (x86)&lt;/td&gt;
&lt;td&gt;Dedicated EC2 (x86)&lt;/td&gt;
&lt;td&gt;Dedicated EC2 (x86)&lt;/td&gt;
&lt;td&gt;Aurora cluster (I/O-Optimized)*&lt;/td&gt;
&lt;td&gt;Aurora cluster (I/O-Optimized)&lt;/td&gt;
&lt;td&gt;Aurora cluster (I/O-Optimized)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS Access&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (100GB)&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;\$11.50 (gp2)&lt;/td&gt;
&lt;td&gt;\$11.50 (gp2)&lt;/td&gt;
&lt;td&gt;\$11.50 (gp2)&lt;/td&gt;
&lt;td&gt;\$10 (I/O-Optimized)&lt;/td&gt;
&lt;td&gt;\$10 (I/O-Optimized)&lt;/td&gt;
&lt;td&gt;\$10 (I/O-Optimized)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Free up to DB size*&lt;/td&gt;
&lt;td&gt;Free up to DB size&lt;/td&gt;
&lt;td&gt;Free up to DB size&lt;/td&gt;
&lt;td&gt;\$0.021/GB*&lt;/td&gt;
&lt;td&gt;\$0.021/GB&lt;/td&gt;
&lt;td&gt;\$0.021/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (500GB)&lt;/td&gt;
&lt;td&gt;\$22.50 (250GB included + $0.09/GB)&lt;/td&gt;
&lt;td&gt;\$45 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$45 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$45 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$45 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$45 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$45 (\$0.09/GB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total/Month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$145&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$186&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$138&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$112.50&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$266&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$193&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$161&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase (Large)&lt;/strong&gt; bundles all costs and removes infrastructure complexity — ideal for fast-moving teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS Reserved&lt;/strong&gt; (especially 3-year) cuts monthly bills by over 50%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aurora Reserved&lt;/strong&gt; costs more, but adds built-in high availability, multi-AZ replication, and better scaling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Heavy Workload (500GB+ Storage, High Throughput)
&lt;/h2&gt;

&lt;p&gt;For mission-critical workloads with large storage, high concurrency, and peak traffic, this tier shows how pricing stacks up across platforms.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase 2XL&lt;/th&gt;
&lt;th&gt;RDS r5.xlarge (On-Demand)&lt;/th&gt;
&lt;th&gt;RDS r5.xlarge (1yr Reserved)&lt;/th&gt;
&lt;th&gt;RDS r5.xlarge (3yr Reserved)&lt;/th&gt;
&lt;th&gt;Aurora r5.xlarge (On-Demand)&lt;/th&gt;
&lt;th&gt;Aurora r5.xlarge (1yr Reserved)&lt;/th&gt;
&lt;th&gt;Aurora r5.xlarge (3yr Reserved)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly Price&lt;/td&gt;
&lt;td&gt;\$410 (flat)*&lt;/td&gt;
&lt;td&gt;\$422&lt;/td&gt;
&lt;td&gt;\$246&lt;/td&gt;
&lt;td&gt;\$170&lt;/td&gt;
&lt;td&gt;\$422&lt;/td&gt;
&lt;td&gt;\$287&lt;/td&gt;
&lt;td&gt;\$210&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;4 vCPU (shared) / 16GB RAM&lt;/td&gt;
&lt;td&gt;4 vCPU / 32GB RAM&lt;/td&gt;
&lt;td&gt;4 vCPU / 32GB RAM&lt;/td&gt;
&lt;td&gt;4 vCPU / 32GB RAM&lt;/td&gt;
&lt;td&gt;4 vCPU / 32GB RAM&lt;/td&gt;
&lt;td&gt;4 vCPU / 32GB RAM&lt;/td&gt;
&lt;td&gt;4 vCPU / 32GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Type&lt;/td&gt;
&lt;td&gt;Shared container&lt;/td&gt;
&lt;td&gt;Dedicated EC2 (x86)&lt;/td&gt;
&lt;td&gt;Dedicated EC2 (x86)&lt;/td&gt;
&lt;td&gt;Dedicated EC2 (x86)&lt;/td&gt;
&lt;td&gt;Aurora cluster (I/O-Optimized)*&lt;/td&gt;
&lt;td&gt;Aurora cluster (I/O-Optimized)&lt;/td&gt;
&lt;td&gt;Aurora cluster (I/O-Optimized)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS Access&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage (500GB)&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;\$57.50 (gp2)&lt;/td&gt;
&lt;td&gt;\$57.50 (gp2)&lt;/td&gt;
&lt;td&gt;\$57.50 (gp2)&lt;/td&gt;
&lt;td&gt;\$50 (I/O-Optimized)&lt;/td&gt;
&lt;td&gt;\$50 (I/O-Optimized)&lt;/td&gt;
&lt;td&gt;\$50 (I/O-Optimized)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IOPS / Throughput&lt;/td&gt;
&lt;td&gt;Included (abstracted)*&lt;/td&gt;
&lt;td&gt;\$100+ (io1 estimated)*&lt;/td&gt;
&lt;td&gt;\$100+ (io1 estimated)&lt;/td&gt;
&lt;td&gt;\$100+ (io1 estimated)&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup (500GB extra)&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;\$47.50*&lt;/td&gt;
&lt;td&gt;\$47.50&lt;/td&gt;
&lt;td&gt;\$47.50&lt;/td&gt;
&lt;td&gt;\$10.50&lt;/td&gt;
&lt;td&gt;\$10.50&lt;/td&gt;
&lt;td&gt;\$10.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth (1TB)&lt;/td&gt;
&lt;td&gt;\$67.50 (250GB included + $0.09/GB)&lt;/td&gt;
&lt;td&gt;\$90 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$90 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$90 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$90 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$90 (\$0.09/GB)&lt;/td&gt;
&lt;td&gt;\$90 (\$0.09/GB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total/Month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$477.5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$717&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$541&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$465&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$572&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$437&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;\$361&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase 2XL&lt;/strong&gt; is an all-inclusive bundle that scales without requiring DBAs or infra tuning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RDS Reserved&lt;/strong&gt; (3yr) delivers maximum cost-efficiency if your workload is stable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aurora Reserved&lt;/strong&gt; is ideal for high-volume, multi-region, or high-availability requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cost Reference for Storage &amp;amp; Compute
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Supabase&lt;/th&gt;
&lt;th&gt;AWS RDS (On-Demand)&lt;/th&gt;
&lt;th&gt;AWS RDS (Reserved)*&lt;/th&gt;
&lt;th&gt;Aurora (I/O-Optimized)*&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;\$0.125/GB&lt;/td&gt;
&lt;td&gt;\$0.115/GB (gp2)*&lt;/td&gt;
&lt;td&gt;\$0.115/GB (gp2)&lt;/td&gt;
&lt;td&gt;\$0.10–\$0.225/GB*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;\$0.095/GB*&lt;/td&gt;
&lt;td&gt;\$0.095/GB&lt;/td&gt;
&lt;td&gt;\$0.021/GB (snapshot)*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bandwidth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;250GB outbound included&lt;/td&gt;
&lt;td&gt;\$0.09/GB outbound&lt;/td&gt;
&lt;td&gt;\$0.09/GB outbound&lt;/td&gt;
&lt;td&gt;\$0.09/GB outbound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compute&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;\$10–\$3,730 (flat)&lt;/td&gt;
&lt;td&gt;\$11–\$1,688&lt;/td&gt;
&lt;td&gt;\$6–\$1,080&lt;/td&gt;
&lt;td&gt;\$67–\$3,376&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reserved pricing&lt;/strong&gt; can reduce compute cost by 30–60%, especially for year-long or 3-year commitments.&lt;/li&gt;
&lt;li&gt;Aurora charges &lt;strong&gt;by I/O operations&lt;/strong&gt;, unless you're on their newer I/O-optimized pricing model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 Explanatory Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;t4g.micro (burstable)&lt;/strong&gt;: AWS uses burstable instances like &lt;code&gt;t4g.micro&lt;/code&gt; for its Free and entry-level RDS tiers. These provide 2 ARM-based vCPUs with a CPU credit system — ideal for low-to-moderate workloads with occasional spikes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;gp2 Storage (AWS)&lt;/strong&gt;: gp2 volumes offer 3 IOPS per GB, with a &lt;strong&gt;minimum baseline of 100 IOPS&lt;/strong&gt; at 20GB. They also include &lt;strong&gt;burst capacity&lt;/strong&gt;, giving better performance than basic shared storage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supabase Flat Pricing&lt;/strong&gt;: Supabase’s pricing includes compute, storage, backups, and bandwidth in a &lt;strong&gt;single monthly rate&lt;/strong&gt; — simplifying cost tracking and reducing surprise bills.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Backup Costs (AWS)&lt;/strong&gt;: RDS provides &lt;strong&gt;free backup storage up to the size of your DB&lt;/strong&gt;. Additional snapshot storage is charged at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;\$0.095/GB for RDS&lt;/li&gt;
&lt;li&gt;\$0.021/GB for Aurora&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Aurora I/O-Optimized&lt;/strong&gt;: This newer Aurora pricing tier eliminates per-I/O charges and instead bills a flat rate per GB stored. Ideal for write-heavy or high-throughput workloads with unpredictable I/O.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;IOPS Costs (RDS)&lt;/strong&gt;: For high-performance needs, RDS users may upgrade to &lt;code&gt;io1&lt;/code&gt; or &lt;code&gt;gp3&lt;/code&gt; storage with provisioned IOPS — typically adding \$100+ per month for workloads requiring consistent low-latency throughput.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reserved Pricing (AWS)&lt;/strong&gt;: RDS and Aurora support &lt;strong&gt;1-year and 3-year reservations&lt;/strong&gt;, reducing monthly compute costs by 30–60%. These are billed upfront or monthly and require workload stability.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Best Choice&lt;/th&gt;
&lt;th&gt;Price Range (Monthly)&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free hobby project&lt;/td&gt;
&lt;td&gt;Supabase Free&lt;/td&gt;
&lt;td&gt;\$0&lt;/td&gt;
&lt;td&gt;No time limit, zero config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Low-cost dev/test DB&lt;/td&gt;
&lt;td&gt;Supabase Pro&lt;/td&gt;
&lt;td&gt;\$25&lt;/td&gt;
&lt;td&gt;Simple flat rate, includes compute, storage, and bandwidth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS trial or AWS-focused team&lt;/td&gt;
&lt;td&gt;AWS Free Tier&lt;/td&gt;
&lt;td&gt;\$0 (12 mo)&lt;/td&gt;
&lt;td&gt;Best value compute &amp;amp; storage for teams already using AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost-sensitive production workload&lt;/td&gt;
&lt;td&gt;RDS Reserved&lt;/td&gt;
&lt;td&gt;\$4.76–\$96&lt;/td&gt;
&lt;td&gt;Long-term commitment cuts RDS compute cost by up to 60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple mid-sized app&lt;/td&gt;
&lt;td&gt;Supabase or RDS&lt;/td&gt;
&lt;td&gt;\$110–\$186&lt;/td&gt;
&lt;td&gt;Supabase for ease, RDS for control &amp;amp; cost tuning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High write/read throughput&lt;/td&gt;
&lt;td&gt;Aurora Reserved&lt;/td&gt;
&lt;td&gt;\$96–\$437&lt;/td&gt;
&lt;td&gt;Built-in performance, replication, and I/O-optimized pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-region / enterprise scale&lt;/td&gt;
&lt;td&gt;Aurora or RDS&lt;/td&gt;
&lt;td&gt;\$161–\$717&lt;/td&gt;
&lt;td&gt;Supports replication, multi-AZ, and enterprise-grade configurations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; offers &lt;strong&gt;simple pricing and easy setup&lt;/strong&gt;, ideal for fast-moving projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS RDS Reserved&lt;/strong&gt; is best for &lt;strong&gt;long-term, cost-optimized workloads&lt;/strong&gt; with more control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aurora&lt;/strong&gt; suits &lt;strong&gt;high-performance, high-availability needs&lt;/strong&gt;, but at a higher cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose based on your need for &lt;strong&gt;simplicity vs control&lt;/strong&gt;, and how stable your usage will be. Supabase works well for early-stage apps; AWS shines for scaled, mature systems.&lt;/p&gt;

&lt;p&gt;Need more than a database? Our next post will compare &lt;a href="https://www.bytebase.com/blog/supabase-vs-aws-pricing/" rel="noopener noreferrer"&gt;Supabase vs AWS pricing across auth, messaging, and more&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>supabase</category>
      <category>database</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How LayerX Achieves “Painless” Governance and Security 🛡️ in the Cloud ☁️</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Fri, 11 Jul 2025 03:57:53 +0000</pubDate>
      <link>https://dev.to/bytebase/how-layerx-achieves-painless-governance-and-security-in-the-cloud-3hpk</link>
      <guid>https://dev.to/bytebase/how-layerx-achieves-painless-governance-and-security-in-the-cloud-3hpk</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2ygcr5ix8ais7z1nl75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2ygcr5ix8ais7z1nl75.png" alt=" " width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At CloudNative Days Summer 2025, Hokuto Hoshi—CISO and Head of SRE/Corporate Engineering at LayerX—shared how the company has built a cloud-native governance framework that balances compliance, security, and developer agility. The presentation, titled &lt;a href="https://speakerdeck.com/kanny/effortless-governance-and-security-enabled-by-the-cloud" rel="noopener noreferrer"&gt;Realizing "Painless" Governance and Security in the Cloud&lt;/a&gt;, resonated with platform teams facing growing audit requirements without wanting to slow down development.&lt;/p&gt;

&lt;h2&gt;
  
  
  About LayerX
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyo2vkvw0zl78brdkotg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyo2vkvw0zl78brdkotg.png" alt=" " width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://layerx.co.jp/" rel="noopener noreferrer"&gt;LayerX&lt;/a&gt; is a Tokyo-based technology company with a mission to enable digitalization across all economic activities. The company operates across several domains, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://bakuraku.jp/" rel="noopener noreferrer"&gt;Bakuraku&lt;/a&gt;: A suite of AI-powered SaaS tools streamlining core enterprise workflows such as expense management, invoice processing, and approval flows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://corp.mitsui-x.com/" rel="noopener noreferrer"&gt;Fintech&lt;/a&gt;: Asset management and securities solutions delivered through a joint venture model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://getaiworkforce.com/" rel="noopener noreferrer"&gt;AI/LLM&lt;/a&gt;: An internal platform that transforms organizational knowledge into structured, retrievable data using large language models.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LayerX builds its products with a strong emphasis on automation, auditability, and developer experience—making it a prime example of a cloud-native enterprise balancing innovation with compliance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Governance Without the Pain
&lt;/h2&gt;

&lt;p&gt;For many organizations, audit readiness still means manually collecting logs, managing permissions through spreadsheets, and retrofitting access controls into existing workflows. LayerX takes a different approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Automate controls using infrastructure-as-code and cloud-native platforms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integrate security practices directly into development workflows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maintain transparency and explainability for auditors and engineers alike&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Treat compliance not as a checkbox but as part of system reliability&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Their implementation spans the entire stack—from user account management to infrastructure provisioning, from application deployment to database change management.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Identity to Infrastructure: A Systems Approach
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SSO as a Security Backbone
&lt;/h3&gt;

&lt;p&gt;LayerX uses &lt;a href="https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id" rel="noopener noreferrer"&gt;Microsoft Entra ID&lt;/a&gt; to unify identity across systems. Employee onboarding and offboarding are fully automated via HR data and Notion, with Slack-integrated workflows for approvals. Access is consistently enforced through SSO across all internal tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Group-Based Permissioning
&lt;/h3&gt;

&lt;p&gt;User groups are defined via &lt;a href="https://smarthr.jp/" rel="noopener noreferrer"&gt;SmartHR&lt;/a&gt; and translated to &lt;a href="https://developer.hashicorp.com/terraform/language" rel="noopener noreferrer"&gt;Terraform HCL&lt;/a&gt;, then synced to Entra ID. This bridges HR systems with access control, enabling scalable role-based access patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time-Bound Privilege Escalation
&lt;/h3&gt;

&lt;p&gt;To handle temporary elevated permissions, LayerX leverages &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/pim-configure" rel="noopener noreferrer"&gt;Entra PIM&lt;/a&gt;, allowing just-in-time access with automatic expiration—a modern answer to long-standing privilege management issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change Management in GitHub
&lt;/h3&gt;

&lt;p&gt;Application and infrastructure changes are governed by pull request approvals using &lt;a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners" rel="noopener noreferrer"&gt;GitHub's CODEOWNERS&lt;/a&gt;. Even emergency changes are logged and traceable, and deployments are automated via tools like &lt;a href="https://github.com/kayac/ecspresso" rel="noopener noreferrer"&gt;ecspresso&lt;/a&gt; and &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; within a monorepo setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unified Logging and Simplified Audits
&lt;/h3&gt;

&lt;p&gt;Logs from &lt;a href="https://aws.amazon.com/cloudtrail/" rel="noopener noreferrer"&gt;AWS CloudTrail&lt;/a&gt;, Entra ID, &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/athena/" rel="noopener noreferrer"&gt;Amazon Athena&lt;/a&gt; are aggregated and searchable via APIs and CLI commands. LayerX stores logs in &lt;a href="https://www.snowflake.com/" rel="noopener noreferrer"&gt;Snowflake&lt;/a&gt;, making it easy to visualize and retrieve audit evidence. Log extraction is automated—no more ad hoc queries or manual exports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Governance with Bytebase
&lt;/h2&gt;

&lt;p&gt;Traditionally, database operations are disconnected from CI/CD and governance workflows. LayerX addressed this gap by adopting &lt;a href="https://dev.to/"&gt;Bytebase&lt;/a&gt; to introduce structured, auditable change management for their databases.&lt;/p&gt;

&lt;p&gt;With Bytebase, LayerX can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Review and approve schema changes through a web interface or API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maintain audit trails for all SQL operations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Eliminate the need for bastion hosts by shifting to a secure, review-based model&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This brings the same rigor to database operations that exist for code and infrastructure—an essential step for aligning security, reliability, and developer velocity.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Model for Modern Platform Teams
&lt;/h2&gt;

&lt;p&gt;LayerX shows that auditability and agility are not mutually exclusive. By embedding governance into the developer workflow and codifying it through infrastructure and policy engines, they’ve created a foundation that’s secure, scalable, and operationally efficient.&lt;/p&gt;

&lt;p&gt;For teams managing database changes, LayerX’s use of Bytebase highlights a growing trend: bringing CI/CD, approval workflows, and visibility to one of the last remaining blind spots in DevSecOps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of Tools and Vendors Used
&lt;/h2&gt;

&lt;p&gt;What LayerX has accomplished is a pragmatic example of how modern engineering practices, when paired with the right tools, can turn governance from a bottleneck into a built-in strength.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool / Platform&lt;/th&gt;
&lt;th&gt;Vendor&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Entra ID&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;SSO, identity provider, group and access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SmartHR&lt;/td&gt;
&lt;td&gt;SmartHR&lt;/td&gt;
&lt;td&gt;HR system for managing employee roles and attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;HashiCorp&lt;/td&gt;
&lt;td&gt;Infrastructure-as-code, role syncing with IdP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entra PIM (Privileged Identity Mgmt)&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;td&gt;Time-limited elevated access with approvals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub + CODEOWNERS&lt;/td&gt;
&lt;td&gt;Microsoft (GitHub)&lt;/td&gt;
&lt;td&gt;Pull request approval workflow for app and infra changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ecspresso&lt;/td&gt;
&lt;td&gt;KAYAC&lt;/td&gt;
&lt;td&gt;ECS deployment tool integrated with GitHub and Terraform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bytebase&lt;/td&gt;
&lt;td&gt;Bytebase&lt;/td&gt;
&lt;td&gt;CI/CD for database schema and data changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS CloudTrail&lt;/td&gt;
&lt;td&gt;Amazon Web Services&lt;/td&gt;
&lt;td&gt;Logging of AWS resource activities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Athena&lt;/td&gt;
&lt;td&gt;Amazon Web Services&lt;/td&gt;
&lt;td&gt;Serverless log querying and analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;Data warehouse for storing and visualizing audit logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;System monitoring and additional log aggregation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;Salesforce&lt;/td&gt;
&lt;td&gt;Notification and approval workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;td&gt;Internal metadata and account provisioning source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI / APIs / Scripts&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;td&gt;Automating log extraction and compliance tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>security</category>
      <category>productivity</category>
      <category>database</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How to Configure MariaDB 🦦 SSL Connection 🔌</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Wed, 02 Jul 2025 06:18:30 +0000</pubDate>
      <link>https://dev.to/bytebase/how-to-configure-mariadb-ssl-connection-1cg</link>
      <guid>https://dev.to/bytebase/how-to-configure-mariadb-ssl-connection-1cg</guid>
      <description>&lt;p&gt;This tutorial shows you how to configure MariaDB SSL connection using self-signed certificates. You'll learn to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate SSL certificates (CA, server, client)&lt;/li&gt;
&lt;li&gt;Configure MariaDB server for SSL&lt;/li&gt;
&lt;li&gt;Test SSL connections from clients&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify MariaDB installation&lt;/span&gt;
mariadb &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Verify OpenSSL installation&lt;/span&gt;
openssl version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you have &lt;a href="https://mariadb.org/download/" rel="noopener noreferrer"&gt;MariaDB&lt;/a&gt; and &lt;a href="https://www.openssl.org/source/" rel="noopener noreferrer"&gt;OpenSSL&lt;/a&gt; installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate SSL Related Files
&lt;/h2&gt;

&lt;h3&gt;
  
  
  OpenSSL Config
&lt;/h3&gt;

&lt;p&gt;Set up the configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;req.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
[ req ]
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no
[ req_distinguished_name ]
C = CN
ST = GD
O = Bytebase
CN = root
[ v3_ca ]
basicConstraints = critical,CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
[ v3_req ]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[ alt_names ]
IP.1 = YOUR_SERVER_IP
DNS.1 = YOUR_SERVER_HOSTNAME
DNS.2 = localhost
IP.2 = 127.0.0.1
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replace &lt;code&gt;YOUR_SERVER_IP&lt;/code&gt; with your actual server IP address. You can find it with &lt;code&gt;ifconfig&lt;/code&gt; or &lt;code&gt;ip addr show&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate Certificates
&lt;/h3&gt;

&lt;p&gt;Generate Root CA key and certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; ca-key.pem 2048
openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; ca-key.pem &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 36500 &lt;span class="nt"&gt;-out&lt;/span&gt; ca-cert.pem &lt;span class="nt"&gt;-extensions&lt;/span&gt; &lt;span class="s1"&gt;'v3_ca'&lt;/span&gt; &lt;span class="nt"&gt;-config&lt;/span&gt; req.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate Server key and certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; server-key.pem 2048
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; server-key.pem &lt;span class="nt"&gt;-out&lt;/span&gt; server-req.pem &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=CN/ST=GD/O=Bytebase/CN=YOUR_SERVER_IP"&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 36500 &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-extensions&lt;/span&gt; v3_req &lt;span class="nt"&gt;-CA&lt;/span&gt; ca-cert.pem &lt;span class="nt"&gt;-CAkey&lt;/span&gt; ca-key.pem &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; server-req.pem &lt;span class="nt"&gt;-out&lt;/span&gt; server-cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replace YOUR_SERVER_IP with your real server IP.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Generate Client key and certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; client-key.pem 2048
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; client-key.pem &lt;span class="nt"&gt;-out&lt;/span&gt; client-req.pem &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=CN/ST=GD/O=Bytebase/CN=mariadb-client"&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 36500 &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-extensions&lt;/span&gt; v3_req &lt;span class="nt"&gt;-CA&lt;/span&gt; ca-cert.pem &lt;span class="nt"&gt;-CAkey&lt;/span&gt; ca-key.pem &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; client-req.pem &lt;span class="nt"&gt;-out&lt;/span&gt; client-cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure MariaDB Server
&lt;/h2&gt;

&lt;p&gt;Copy SSL files and set permissions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For macOS (Homebrew):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For Apple Silicon Macs&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/homebrew/etc/mariadb/ssl
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;ca-cert.pem server-cert.pem server-key.pem /opt/homebrew/etc/mariadb/ssl/
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:admin /opt/homebrew/etc/mariadb/ssl/
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /opt/homebrew/etc/mariadb/ssl/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;-key&lt;/span&gt;.pem
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 /opt/homebrew/etc/mariadb/ssl/ca-cert.pem /opt/homebrew/etc/mariadb/ssl/server-cert.pem

&lt;span class="c"&gt;# For Intel Macs&lt;/span&gt;
&lt;span class="c"&gt;# sudo mkdir -p /usr/local/etc/mariadb/ssl&lt;/span&gt;
&lt;span class="c"&gt;# sudo cp ca-cert.pem server-cert.pem server-key.pem /usr/local/etc/mariadb/ssl/&lt;/span&gt;
&lt;span class="c"&gt;# sudo chown -R $(whoami):admin /usr/local/etc/mariadb/ssl/&lt;/span&gt;
&lt;span class="c"&gt;# sudo chmod 600 /usr/local/etc/mariadb/ssl/*-key.pem&lt;/span&gt;
&lt;span class="c"&gt;# sudo chmod 644 /usr/local/etc/mariadb/ssl/ca-cert.pem /usr/local/etc/mariadb/ssl/server-cert.pem&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Linux systems:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/mariadb/ssl
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;ca-cert.pem server-cert.pem server-key.pem /etc/mariadb/ssl/
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;mysql:mysql /etc/mariadb/ssl/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /etc/mariadb/ssl/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;-key&lt;/span&gt;.pem
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 /etc/mariadb/ssl/ca-cert.pem /etc/mariadb/ssl/server-cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit MariaDB configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For macOS (Apple Silicon)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /opt/homebrew/etc/my.cnf

&lt;span class="c"&gt;# For macOS (Intel)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /usr/local/etc/my.cnf

&lt;span class="c"&gt;# For Linux (Ubuntu/Debian)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/mysql/mariadb.conf.d/50-server.cnf

&lt;span class="c"&gt;# For Linux (CentOS/RHEL)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/my.cnf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add SSL configuration:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For macOS (Apple Silicon):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mysqld]&lt;/span&gt;
&lt;span class="py"&gt;ssl-ca&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/opt/homebrew/etc/mariadb/ssl/ca-cert.pem&lt;/span&gt;
&lt;span class="py"&gt;ssl-cert&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/opt/homebrew/etc/mariadb/ssl/server-cert.pem&lt;/span&gt;
&lt;span class="py"&gt;ssl-key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/opt/homebrew/etc/mariadb/ssl/server-key.pem&lt;/span&gt;
&lt;span class="py"&gt;bind-address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0&lt;/span&gt;
&lt;span class="py"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3306&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For macOS (Intel):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mysqld]&lt;/span&gt;
&lt;span class="py"&gt;ssl-ca&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/usr/local/etc/mariadb/ssl/ca-cert.pem&lt;/span&gt;
&lt;span class="py"&gt;ssl-cert&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/usr/local/etc/mariadb/ssl/server-cert.pem&lt;/span&gt;
&lt;span class="py"&gt;ssl-key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/usr/local/etc/mariadb/ssl/server-key.pem&lt;/span&gt;
&lt;span class="py"&gt;bind-address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0&lt;/span&gt;
&lt;span class="py"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3306&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Linux systems:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mysqld]&lt;/span&gt;
&lt;span class="py"&gt;ssl-ca&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/etc/mariadb/ssl/ca-cert.pem&lt;/span&gt;
&lt;span class="py"&gt;ssl-cert&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/etc/mariadb/ssl/server-cert.pem&lt;/span&gt;
&lt;span class="py"&gt;ssl-key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/etc/mariadb/ssl/server-key.pem&lt;/span&gt;
&lt;span class="py"&gt;bind-address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0&lt;/span&gt;
&lt;span class="py"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3306&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart MariaDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For macOS (Homebrew)&lt;/span&gt;
brew services restart mariadb

&lt;span class="c"&gt;# For Linux (systemd)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart mariadb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test SSL Connection
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mariadb &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that you'll be entering MariaDB CLI. You can also verify remote connection by replacing the &lt;code&gt;localhost&lt;/code&gt; above with your server IP to connect. Check your SSL connection with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seeing something like &lt;code&gt;SSL: Cipher in use is TLS_AES_256_GCM_SHA384, cert is OK&lt;/code&gt;, so that the SSL connection is ready.&lt;/p&gt;

&lt;p&gt;Or use command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;STATUS&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'Ssl_version'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;---------------+---------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Variable_name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;---------------+---------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Ssl_version&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;TLSv1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;---------------+---------+&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;006&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You have successfully configured SSL for MariaDB:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generated CA, server, and client certificates&lt;/li&gt;
&lt;li&gt;Configured MariaDB with SSL settings&lt;/li&gt;
&lt;li&gt;Tested secure connections from clients&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your MariaDB server now accepts encrypted connections only.&lt;/p&gt;

</description>
      <category>ssl</category>
      <category>mariadb</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Features I Wish Postgres 🐘 Had but MySQL 🐬 Already Has 😼</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Thu, 26 Jun 2025 07:20:30 +0000</pubDate>
      <link>https://dev.to/bytebase/features-i-wish-postgres-had-but-mysql-already-has-216b</link>
      <guid>https://dev.to/bytebase/features-i-wish-postgres-had-but-mysql-already-has-216b</guid>
      <description>&lt;p&gt;At Bytebase, we work extensively with both Postgres and MySQL. In our &lt;a href="https://dev.to/blog/features-i-wish-mysql-had-but-postgres-already-has"&gt;previous post&lt;/a&gt;, we highlighted features where Postgres shines compared to MySQL. In this post, we'll examine where MySQL excels and Postgres falls short.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performant Storage Engine for Write-Intensive Workloads
&lt;/h2&gt;

&lt;p&gt;Postgres' heap-based storage with tuple versioning MVCC creates more overhead for write-intensive scenarios (write amplification). While Postgres has made improvements with features like HOT updates, it still generates more WAL (Write-Ahead Log) traffic and requires more frequent VACUUM operations under heavy write loads.&lt;/p&gt;

&lt;p&gt;On the other hand, MySQL employs clustered index storage and undo logging, which is more performant especially for write heavy workload.&lt;/p&gt;

&lt;p&gt;The comparison is detailed in &lt;a href="https://www.uber.com/blog/postgres-to-mysql-migration/" rel="noopener noreferrer"&gt;Uber's switch From Postgres to MySQL&lt;/a&gt; and &lt;a href="https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-Postgres-we-hate-the-most.html" rel="noopener noreferrer"&gt;The Part of Postgres We Hate the Most&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Postgres users need to deal with the infamous &lt;a href="https://blog.sentry.io/transaction-id-wraparound-in-postgres/" rel="noopener noreferrer"&gt;transaction ID wraparound&lt;/a&gt;, and the not-so-famous &lt;a href="https://metronome.com/blog/root-cause-analysis-Postgres-multixact-member-exhaustion-incidents-may-2025" rel="noopener noreferrer"&gt;MultiXact member exhaustion&lt;/a&gt;. &lt;a href="https://github.com/orioledb/orioledb" rel="noopener noreferrer"&gt;OrioleDB&lt;/a&gt; is tackling these issues, but it's still years until it reaches the maturity of MySQL's InnoDB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replication
&lt;/h2&gt;

&lt;p&gt;MySQL's Group Replication provides both single-primary and multi-primary replication with automatic failover and conflict detection built-in. It handles network partitions gracefully with built-in quorum logic, automatically determining which partition remains writable.&lt;/p&gt;

&lt;p&gt;Postgres relies on external tools like Patroni, pg_auto_failover for similar functionality. These solutions work but require non-trivial operational overhead and expertise to configure and maintain properly.&lt;/p&gt;

&lt;p&gt;Postgres' logical replication, introduced in version 10, is also still catching up. Its most significant limitation is DDL operations are not replicated - they must be applied manually to subscribers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invisible Indexes
&lt;/h2&gt;

&lt;p&gt;MySQL's invisible indexes allow you to test index effectiveness without impacting existing queries or disable indexes temporarily - a crucial feature for production database optimization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- MySQL: Create invisible index for testing&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_status&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;INVISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Test queries with the invisible index&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;SESSION&lt;/span&gt; &lt;span class="n"&gt;optimizer_switch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'use_invisible_indexes=on'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Make index visible after confirming it helps&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_status&lt;/span&gt; &lt;span class="n"&gt;VISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They also help with index maintenance strategies. You can make an index invisible before dropping it, ensuring no queries break, then safely remove it later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Make index invisible to see if it hurts&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_status&lt;/span&gt; &lt;span class="n"&gt;INVISIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Then drop it if it doesn't break queries&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_status&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postgres doesn't provide built-in syntax to enable/disable an index. Though you can toggle &lt;code&gt;indisvalid&lt;/code&gt; from the &lt;code&gt;pg_index&lt;/code&gt; table as a workaround.&lt;/p&gt;

&lt;h2&gt;
  
  
  Online Schema Migration Tooling
&lt;/h2&gt;

&lt;p&gt;MySQL's ecosystem provides mature, production-ready tools for online schema changes that Postgres still lacks.&lt;/p&gt;

&lt;p&gt;If your table doesn't have foreign keys, you can use GitHub's trigger-less &lt;a href="https://github.com/github/gh-ost/" rel="noopener noreferrer"&gt;gh-ost&lt;/a&gt;, otherwise, Percona's trigger-based &lt;a href="https://docs.percona.com/percona-toolkit/pt-online-schema-change.html" rel="noopener noreferrer"&gt;pt-online-schema-change&lt;/a&gt; is also a battle-tested solution.&lt;/p&gt;

&lt;p&gt;The Postgres community has developed several solutions, but none has matched the adoption rate of MySQL's gh-ost or pt-online-schema-change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connection Handling
&lt;/h2&gt;

&lt;p&gt;While Postgres' process-per-connection provides better isolation, it consumes more memory per connection, incurs context switching overhead, and is more prune to hit connection limit.&lt;/p&gt;

&lt;p&gt;MySQL's thread-per-connection model is subject to entire server crashes caused by a query of death, but is more scalable.&lt;/p&gt;

&lt;p&gt;There is a comprehensive discussion in the Postgres community about making the switch &lt;a href="https://www.Postgres.org/message-id/flat/31cc6df9-53fe-3cd9-af5b-ac0d801163f4%40iki.fi" rel="noopener noreferrer"&gt;Let's make Postgres multi-threaded&lt;/a&gt;. But because the process model is too deeply coupled with the rest of Postgres, it will be quite a challenging task to tackle.&lt;/p&gt;




&lt;p&gt;MySQL remains the most popular open source database today, serving as the de facto database of the internet era. Looking back, two pillars drove its popularity:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pragmatic core team.&lt;/strong&gt; The original MySQL team prioritized features based on real-world use cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Replication, the most critical building block for scalability and fast failover on commodity hardware, was introduced in version 3.23 in 2001—nearly a decade before Postgres added replication in 2010 (version 9.0).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the core team lacked bandwidth to build a better storage engine, they designed a pluggable storage engine architecture that opened the door to InnoDB.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Vibrant community.&lt;/strong&gt; When internet companies experienced hypergrowth and needed an open source database that was performant and scalable on commodity hardware, MySQL was the obvious choice. Google's initial ads system, Twitter, Facebook, Alibaba, and GitHub all chose MySQL. Though exceptions like Instagram chose Postgres, most successful internet-era companies selected MySQL and contributed enhancements like MHA, RocksDB, gh-ost, and replication improvements. This created a generation of MySQL experts advocating the technology and spawned dedicated consulting businesses like Percona, which provided educational resources and indispensable tools like the Percona toolkit.&lt;/p&gt;

&lt;p&gt;Tides are changing, and Postgres has become the new hotness in the AI era. While MySQL retains several architectural advantages over Postgres, will it hold the fort or become obsolete within 10 years?&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>mysql</category>
      <category>database</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Features I Wish MySQL Had but Postgres 🐘 Already Has 😻</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Tue, 10 Jun 2025 07:04:15 +0000</pubDate>
      <link>https://dev.to/bytebase/features-i-wish-mysql-had-but-postgres-already-has-4986</link>
      <guid>https://dev.to/bytebase/features-i-wish-mysql-had-but-postgres-already-has-4986</guid>
      <description>&lt;p&gt;At Bytebase, we work extensively with PostgreSQL and MySQL. Both databases have pros and cons. In this post, we’ll highlight where PostgreSQL shines and MySQL falls short.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transactional DDL
&lt;/h2&gt;

&lt;p&gt;One of PostgreSQL's most underrated features is its support for transactional DDL (Data Definition Language). In PostgreSQL, you can wrap schema changes in transactions and roll them back if something goes wrong. Even better, PostgreSQL supports savepoints within DDL transactions for fine-grained control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL: This works!&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;email_verified&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SAVEPOINT&lt;/span&gt; &lt;span class="n"&gt;after_column_add&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_users_email_verified&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email_verified&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Oops, index creation failed, but we can keep the column&lt;/span&gt;
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;SAVEPOINT&lt;/span&gt; &lt;span class="n"&gt;after_column_add&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In MySQL, DDL statements are auto-committed, meaning there's no going back once you execute them. This makes database migrations risky, especially in production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Types and Domains
&lt;/h2&gt;

&lt;p&gt;PostgreSQL allows you to create custom data types and domains, providing better data modeling capabilities and stronger type safety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL: Create custom types&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;mood&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sad'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ok'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'happy'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DOMAIN&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="o"&gt;~*&lt;/span&gt; &lt;span class="s1"&gt;'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s1"&gt;[A-Za-z]{2,}$'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;-- Custom domain with validation&lt;/span&gt;
    &lt;span class="n"&gt;current_mood&lt;/span&gt; &lt;span class="n"&gt;mood&lt;/span&gt;  &lt;span class="c1"&gt;-- Custom enum type&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MySQL's ENUM support is limited and doesn't offer the same flexibility. Custom domains with built-in validation rules help enforce data integrity at the database level, reducing the need for application-level validation and ensuring consistency across all applications that access the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Array Types
&lt;/h2&gt;

&lt;p&gt;PostgreSQL's native array support is incredibly powerful for storing and querying lists of values without requiring separate tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL: Arrays are first-class citizens&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;  &lt;span class="c1"&gt;-- Array of tags&lt;/span&gt;
    &lt;span class="n"&gt;view_counts&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="c1"&gt;-- Array of daily view counts&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Query with array operations&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="s1"&gt;'postgresql'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view_counts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's worth noting that PostgreSQL arrays don't support referential integrity constraints yet - you can't have foreign key relationships on array elements. Despite this limitation, native array support eliminates the need for many-to-many relationship tables in certain scenarios and can significantly simplify data models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Table Expressions (CTEs)
&lt;/h2&gt;

&lt;p&gt;PostgreSQL has supported CTEs since version 8.4 (2009), while MySQL only added basic CTE support in version 8.0 (2018). PostgreSQL's implementation is more mature and feature-complete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL: Recursive CTEs for hierarchical data&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;RECURSIVE&lt;/span&gt; &lt;span class="n"&gt;employee_hierarchy&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- Base case: top-level managers&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;

    &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;

    &lt;span class="c1"&gt;-- Recursive case: subordinates&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;employee_hierarchy&lt;/span&gt; &lt;span class="n"&gt;eh&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;eh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employee_hierarchy&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL's CTE implementation supports more advanced features like &lt;code&gt;MATERIALIZED&lt;/code&gt; and &lt;code&gt;NOT MATERIALIZED&lt;/code&gt; hints, giving you control over query optimization strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Row Level Security (RLS)
&lt;/h2&gt;

&lt;p&gt;PostgreSQL's Row Level Security allows you to implement fine-grained access control directly at the database level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL: Enable RLS and create policies&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Policy for user-specific data&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;user_documents&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
    &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt;
    &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.current_user_id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Policy for multi-tenant applications&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;tenant_isolation&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
    &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt;
    &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.tenant_id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Users can only see their own documents automatically&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- Only returns user's documents in their tenant&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While RLS is excellent for simple &lt;code&gt;user_id&lt;/code&gt; or &lt;code&gt;tenant_id&lt;/code&gt; filtering, it becomes complex for more sophisticated authorization rules involving roles, permissions, or dynamic relationships. However, it still serves as a valuable defense-in-depth mechanism in your database security chain, ensuring that even if application-level security fails, sensitive data remains protected.&lt;/p&gt;

&lt;p&gt;MySQL lacks built-in RLS, forcing developers to implement access control in application code or through complex view structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Partial Indexes
&lt;/h2&gt;

&lt;p&gt;PostgreSQL allows you to create indexes with WHERE clauses, making them smaller and more efficient for specific query patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL: Index only active users&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_active_users&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Index only pending orders&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_pending_orders&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MySQL requires you to index the entire column, leading to larger indexes and potentially slower performance when you only need to optimize queries for a subset of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spatial Support
&lt;/h2&gt;

&lt;p&gt;While both databases support spatial data, PostgreSQL's PostGIS extension provides far more comprehensive geospatial capabilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL with PostGIS: Advanced spatial queries&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;ST_Distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;935242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;730610&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;distance_km&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;restaurants&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ST_DWithin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;935242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;730610&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;-- Within 1km&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;935242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;730610&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;-- KNN search&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MySQL's spatial support, while improving, doesn't match the depth and performance of PostGIS. For applications requiring serious geospatial functionality, PostgreSQL with PostGIS is the clear winner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector Support
&lt;/h2&gt;

&lt;p&gt;With the rise of AI, PostgreSQL has embraced vector storage and similarity search through pgvector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- PostgreSQL with pgvector: Store and search embeddings&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;VECTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;-- OpenAI embedding dimension&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Find similar documents using cosine similarity&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'[0.1, 0.2, ...]'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;similarity&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'[0.1, 0.2, ...]'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MySQL recently added vector support in version 9.0 with basic VECTOR data types and limited distance functions, but PostgreSQL's pgvector extension remains more mature with broader operator support, better indexing options (IVFFlat, HNSW), and a richer ecosystem of vector operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parser
&lt;/h2&gt;

&lt;p&gt;PostgreSQL adheres more closely to SQL standards and provides better error messages and more predictable behavior. MySQL's parser has historically been more lenient, which can lead to subtle bugs and makes it harder to write portable SQL.&lt;/p&gt;

&lt;p&gt;For tooling developers, the &lt;a href="https://github.com/pganalyze/libpg_query" rel="noopener noreferrer"&gt;libpg_query&lt;/a&gt; project extracts PostgreSQL's actual parser code, providing 100% compatibility with PostgreSQL's SQL parsing behavior. For MySQL, the most widely used parser is from &lt;a href="https://github.com/pingcap/tidb/tree/master/pkg/parser" rel="noopener noreferrer"&gt;TiDB&lt;/a&gt;, but it has compatibility differences since it was built specifically for TiDB's needs rather than pure MySQL compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Openness
&lt;/h2&gt;

&lt;p&gt;PostgreSQL pairs a permissive PostgreSQL License (akin to BSD) with an extensible architecture, giving developers full freedom to embed, modify, or commercialize the database without license friction. Its community operates in the open: design discussions happen on the public &lt;a href="https://www.postgresql.org/list/pgsql-hackers/" rel="noopener noreferrer"&gt;pgsql-hackers mailing list&lt;/a&gt;, and every commit carries an explanatory message—see, for instance, the recent PG 18 additions of &lt;a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=83ea6c54025bea67bcd4949a6d58d3fc11c3e21b" rel="noopener noreferrer"&gt;virtual generated columns&lt;/a&gt; and &lt;a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=a379061a22a8fdf421e1a457cc6af8503def6252" rel="noopener noreferrer"&gt;NOT VALID NOT NULL constraints&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By contrast, MySQL’s dual-licensing model (GPL plus commercial) restricts downstream choices, and its main public touchpoint is an &lt;a href="https://bugs.mysql.com/" rel="noopener noreferrer"&gt;opaque bug tracker&lt;/a&gt;—some issues are hidden from both the community and the original reporter.&lt;/p&gt;

&lt;p&gt;As MongoDB’s CEO put it on the FY26 Q1 earnings call:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7y1692my7k080kbv8ew6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7y1692my7k080kbv8ew6.png" alt=" " width="632" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Open source, open standard, and most importantly, not owned by any one vendor.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL embodies that ideal in a way MySQL cannot.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>mysql</category>
      <category>sql</category>
      <category>database</category>
    </item>
    <item>
      <title>Postgres 🐘 vs. MySQL 🐬: DDL Transaction Difference</title>
      <dc:creator>Ayra Jett</dc:creator>
      <pubDate>Tue, 03 Jun 2025 13:30:00 +0000</pubDate>
      <link>https://dev.to/bytebase/postgres-vs-mysql-ddl-transaction-difference-2gjo</link>
      <guid>https://dev.to/bytebase/postgres-vs-mysql-ddl-transaction-difference-2gjo</guid>
      <description>&lt;p&gt;Database schema changes are critical operations that require careful planning and execution. The ability to perform these changes safely and reliably is a key consideration when choosing a database management system. In this post, we'll compare how PostgreSQL 17 and MySQL 8 handle Data Definition Language (DDL) transactions, with a focus on atomicity and rollback capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding DDL Transactions
&lt;/h2&gt;

&lt;p&gt;Before diving into the comparison, let's clarify what we mean by DDL transactions. DDL statements can be grouped together and either committed as a unit or rolled back entirely if something goes wrong.&lt;/p&gt;

&lt;p&gt;There are two important concepts to distinguish:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transactional DDL&lt;/strong&gt;: The ability to include DDL statements within a multi-statement transaction block, with the option to commit or roll back all statements together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Atomic DDL&lt;/strong&gt;: The guarantee that individual DDL statements are atomic (all-or-nothing), but not necessarily supporting their inclusion in multi-statement transactions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  PostgreSQL 17: True Transactional DDL
&lt;/h2&gt;

&lt;p&gt;In PostgreSQL 17, DDL operations are fully transactional, meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DDL statements can be included in transaction blocks alongside DML statements&lt;/li&gt;
&lt;li&gt;Multiple DDL operations can be committed or rolled back as a single unit&lt;/li&gt;
&lt;li&gt;Savepoints can be used within transactions that include DDL statements&lt;/li&gt;
&lt;li&gt;If a transaction fails, all DDL changes are rolled back, leaving the database in its original state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are only a few exceptions to this rule: operations on databases and tablespaces themselves (such as &lt;code&gt;CREATE DATABASE&lt;/code&gt; or &lt;code&gt;DROP TABLESPACE&lt;/code&gt;) cannot be rolled back. However, all other catalog operations are reversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  MySQL 8: Atomic DDL
&lt;/h2&gt;

&lt;p&gt;Prior to MySQL 8, DDL operations in MySQL were not atomic at all. If a DDL statement failed partway through execution—such as an &lt;code&gt;ALTER TABLE&lt;/code&gt; operation that was adding multiple columns or indexes—the database could be left in an inconsistent, partially-modified state.&lt;/p&gt;

&lt;p&gt;MySQL 8 introduced a feature called &lt;strong&gt;Atomic DDL&lt;/strong&gt; which represents a significant improvement over previous versions but differs fundamentally from PostgreSQL's approach.&lt;/p&gt;

&lt;p&gt;In MySQL 8, DDL statements are atomic at the statement level, meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Individual DDL statements are either fully completed or fully rolled back&lt;/li&gt;
&lt;li&gt;DDL statements &lt;strong&gt;implicitly commit&lt;/strong&gt; any active transaction before execution&lt;/li&gt;
&lt;li&gt;DDL statements cannot be part of a multi-statement transaction that can be rolled back&lt;/li&gt;
&lt;li&gt;Crash recovery ensures statement-level atomicity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MySQL's atomic DDL is implemented through a special internal &lt;code&gt;DDL_LOG&lt;/code&gt; table in InnoDB that tracks the creation of files and structures during DDL execution. This log is used at commit/rollback time to clean up properly, ensuring that no orphaned files or index trees remain after a crash.&lt;/p&gt;

&lt;p&gt;It's important to note that atomic DDL is only supported in MySQL 8 InnoDB storage engine. For tables using other storage engines, partial updates may still occur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;To better understand these differences, let's run some DDLs that demonstrate how DDL transactions behave in both systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  PostgreSQL 17
&lt;/h3&gt;

&lt;p&gt;Our 1st example makes all DDL operations wrap in a transaction. When we execute the &lt;code&gt;ROLLBACK&lt;/code&gt; command, all tables and indexes are removed as if they were never created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Start a transaction block&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Create a simple table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Add an index&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_username&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Oops! We made a mistake and want to roll back all changes&lt;/span&gt;
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Verify that the table was not created&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;table_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Should return no rows, as the transaction was rolled back&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 2nd example demonstrates how PostgreSQL allows partial rollbacks using &lt;code&gt;SAVEPOINT&lt;/code&gt;, giving developers fine-grained control over schema changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Create a table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Create a savepoint&lt;/span&gt;
&lt;span class="n"&gt;SAVEPOINT&lt;/span&gt; &lt;span class="n"&gt;after_users_table&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Alter the table to add a column&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Oops! We only want to roll back the column addition&lt;/span&gt;
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;after_users_table&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Add a different column instead&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Commit the transaction&lt;/span&gt;
&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MySQL 8
&lt;/h3&gt;

&lt;p&gt;In this MySQL example, despite wrapping the DDL statements in a transaction block, the &lt;code&gt;ROLLBACK&lt;/code&gt; command does not undo the table creation. This is because each DDL statement implicitly commits the transaction before executing, making it impossible to roll back multiple DDL statements as a unit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Try to use a transaction block (note: this won't work as expected for DDL)&lt;/span&gt;
&lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="n"&gt;TRANSACTION&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Create a simple table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Add an index&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_username&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Try to roll back all changes (won't work for DDL statements)&lt;/span&gt;
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Verify that the table was created despite the ROLLBACK&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Will show 'users' table&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, MySQL 8 does provide statement-level atomicity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This will either create all users or none&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'user1'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'password1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'user2'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'password2'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- This will either drop all tables or none&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Let's examine the key differences between PostgreSQL 17 and MySQL 8 in how they handle DDL transactions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;PostgreSQL 17&lt;/th&gt;
&lt;th&gt;MySQL 8&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DDL Transaction Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full transactional DDL&lt;/td&gt;
&lt;td&gt;Atomic DDL (statement-level only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-statement DDL Transactions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DDL Rollback in Transaction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (implicit commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Atomicity of Single DDL Statement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (InnoDB only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Crash Recovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full transaction recovery&lt;/td&gt;
&lt;td&gt;Statement-level recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Savepoints with DDL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exceptions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Database and tablespace operations&lt;/td&gt;
&lt;td&gt;Database directory removal not part of atomic operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Implementation Mechanism&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Write-Ahead Log (WAL)&lt;/td&gt;
&lt;td&gt;DDL_LOG in InnoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PostgreSQL's transactional DDL provides a higher level of safety for complex schema migrations. By wrapping multiple related changes in a transaction, administrators can ensure that the database remains in a consistent state, even if something goes wrong during the migration process. In contrast, MySQL 8 introduces atomic DDL at the statement level, meaning each individual DDL statement is executed as an all-or-nothing operation. While this is a notable improvement over earlier versions of MySQL, it still does not support multi-statement DDL transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;PostgreSQL Wiki: &lt;a href="https://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis" rel="noopener noreferrer"&gt;Transactional DDL in PostgreSQL: A Competitive Analysis&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MySQL Documentation: &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/atomic-ddl.html" rel="noopener noreferrer"&gt;Atomic Data Definition Statement Support&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MySQL Blog: &lt;a href="https://dev.mysql.com/blog-archive/atomic-ddl-in-mysql-8-0/" rel="noopener noreferrer"&gt;Atomic DDL in MySQL 8.0&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MySQL Documentation: &lt;a href="https://dev.mysql.com/doc/refman/8.2/en/cannot-roll-back.html" rel="noopener noreferrer"&gt;Statements That Cannot Be Rolled Back&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>postgres</category>
      <category>mysql</category>
      <category>sql</category>
      <category>database</category>
    </item>
  </channel>
</rss>
