---
title: "Connect Replit to GCP Cloud SQL MySQL: Public IP vs Auth Proxy Setup Guide"
description: "Learn how to connect a Replit Python app to Google Cloud SQL MySQL securely and efficiently. This practical guide covers two connection methods (public IP with authorized networks vs Cloud SQL Auth Proxy), credential management with Replit Secrets, complete Python code examples with pymysql, and troubleshooting tips for common networking and latency issues. Includes SQL setup, connection pooling considerations, and security best practices for avoiding hardcoded passwords."
image: "https://cdn.blackseoanalyzer.com/blog-images/connect-your-replit-app-to-a-gcp-mysql-cloud-database-social.jpg"
---

# Connect Replit to GCP Cloud SQL MySQL: Public IP vs Auth Proxy Setup Guide

Replit quietly made a lot of database tutorials less weird: as of December 2025, Replit is hosted on GCP. That means your "toy app" can sit pretty close to a real Cloud SQL MySQL instance. Less latency. Fewer network hops. Fewer excuses for why `SELECT 1` takes half a second.

This post is the practical path: get a [Replit Python app talking to a GCP Cloud SQL MySQL database](https://www.sethserver.com/programming/insanely-rapid-ai-assisted-development-with-replit.html) without turning your project into a security-themed escape room.

## Prereqs (don't skip these)

You need:

- A **GCP project with billing enabled**
- A **Cloud SQL MySQL instance** already running
- A **database** and a **user** created
- A **Replit app** (I'll show Python)

If you don't have the DB/user yet, go create them now. If you're tempted to use the `root` user, take a walk and think about your choices.

Here's the checklist version of "created":

1. **Cloud SQL instance exists**
   In the Cloud Console: Cloud SQL - Instances - your instance. Confirm it's MySQL, and it's in a region you actually want.
2. **Database exists**
   Cloud SQL - your instance - Databases - Create database. Example: `appdb`.
3. **User exists**
   Cloud SQL - your instance - Users - Add user account. Example: `appuser`. Give it a real password. Don't reuse your Gmail password. Yes, I have to say this.

Smart move: give the user only the permissions your app needs. For a prototype, you might go broader. For anything real, tighten it.

If you prefer SQL over clicking:

```sql
CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE USER 'appuser'@'%' IDENTIFIED BY 'a-long-password-here';

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX
ON appdb.* TO 'appuser'@'%';

FLUSH PRIVILEGES;
```

That `@'%'` is convenient because it allows connections from anywhere. It's also why you need to be strict about network access.

## Two ways to connect (pick your pain)

### Option A: Public IP + Authorized Networks (fastest setup)

This is the "I want it working in 15 minutes" option.

Cloud SQL gets a public IP. Your app connects like it would to any MySQL server. You limit inbound access by whitelisting IPs in **Authorized networks**.

Steps:

1. In Cloud SQL, enable **Public IP**
   Cloud SQL - your instance - Connections - Networking.
   Make sure "Public IP" is enabled.
2. Add an **Authorized network** (an allowed inbound IP/CIDR)
   Same screen: "Authorized networks" - Add network.
   You'll enter something like:
3. `203.0.113.42/32` (single IP)
4. or a CIDR range if you really mean it
5. Use normal MySQL credentials from your app
   Host = the instance public IP
   Port = 3306
   Username/password = the user you created
   Database = `appdb`

For hobby apps and prototypes, this is fine. For production, open networks eventually bite you. Not "maybe." Eventually.

**The annoying part:** figuring out what IP your Replit app is actually coming from.

A few realities:

- Replit workloads may not have a stable outbound IP forever.
- Even if you find an IP today, it can change tomorrow. Then Cloud SQL blocks you. You get timeouts. You question your career.

If you're doing Option A, make debugging easy. Run this in Replit:

```python
import urllib.request

print(urllib.request.urlopen("https://api.ipify.org").read().decode())
```

Take that IP and add it to Authorized networks as `/32`. If it changes later, you'll repeat the ritual.

**Option A tradeoffs:**
- Pros: simplest, fastest to set up, no proxy process
- Cons: IP whitelisting pain, public exposure, passwords still in play

### Option B: Cloud SQL Auth Proxy (more secure)

This is the "I don't want to sprinkle database access across the internet" option.

The Cloud SQL Auth Proxy creates a local TCP listener (often `127.0.0.1:3306`, or another port you choose). Your app connects to `localhost`. The proxy handles the secure connection to Cloud SQL using IAM credentials.

- You run the **Cloud SQL Auth Proxy** and connect to `localhost`.
- Auth can be handled via IAM/service identity instead of "passwords taped under the keyboard."

Passwords in config files are a slow-moving mess. Even if you swear you'll rotate them later. You won't. Then you'll be digging through git history looking for that password you "only used for a minute."

Cloud SQL Auth Proxy docs (shockingly decent):
https://cloud.google.com/sql/docs/mysql/sql-proxy

**How this usually works on Replit:**

Replit isn't a full VM you manage like GCE, so you have to decide:
- Where the proxy binary lives (download at runtime, or check it in-don't check in secrets)
- How you start it (a "Run" command that starts proxy + app, or a small supervisor script)
- How you provide credentials (service account JSON in Secrets, or workload identity if supported)

A pragmatic setup for many small apps:
- Store a service account JSON in Replit Secrets (as a single string)
- Write it to a file at runtime
- Start the proxy pointing at that file
- Connect your app to `127.0.0.1`

Yes, that still involves a secret. It's still better than scattering DB passwords across configs and laptops.

**Option B tradeoffs:**
- Pros: tighter security model, no public IP whitelisting games, better long-term story
- Cons: more moving parts, you have to run a proxy process, you need IAM set up correctly

If strangers will use your app, Option B is where you should land.

## Store credentials in Replit Secrets (not in code)

In Replit, go to **Secrets** and set:

- `DB_HOST` = Cloud SQL public IP (Option A) *or* `127.0.0.1` (Option B)
- `DB_PORT` = `3306` (or whatever local port your proxy uses)
- `DB_NAME` = `appdb`
- `DB_USER` = `appuser`
- `DB_PASS` = your password (Option A, and sometimes Option B depending on how you auth)

Never store passwords in Git. Git is forever. Your future self will not thank you.

Another practical point: don't commit "almost secrets" either. Instance IPs, usernames, and database names aren't as sensitive as passwords, but they still make your setup easier to attack and harder to rotate. Secrets gives you one place to change things without a commit.

## Python example (pymysql)

Install:

```bash
pip install pymysql
```

Then:

```python
import os
import pymysql
from pymysql.cursors import DictCursor

def get_conn():
    return pymysql.connect(
        host=os.environ["DB_HOST"],
        port=int(os.environ.get("DB_PORT", "3306")),
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASS"],
        database=os.environ["DB_NAME"],
        cursorclass=DictCursor,
        connect_timeout=10,
        read_timeout=10,
        write_timeout=10,
        autocommit=True,
        charset="utf8mb4",
    )

def main():
    conn = get_conn()
    try:
        with conn.cursor() as cur:
            cur.execute("SELECT NOW() AS server_time;")
            row = cur.fetchone()
            print(row)
    finally:
        conn.close()

if __name__ == "__main__":
    main()
```

That `SELECT NOW()` test is boring. Good. Boring is stable.

### A more realistic example (create table + insert + query)

This is where people find out they forgot `autocommit=True`, or they're using the wrong database, or their user has no permissions.

```python
import os
import pymysql
from pymysql.cursors import DictCursor

def conn():
    return pymysql.connect(
        host=os.environ["DB_HOST"],
        port=int(os.environ.get("DB_PORT", "3306")),
        user=os.environ["DB_USER"],
        password=os.environ["DB_PASS"],
        database=os.environ["DB_NAME"],
        cursorclass=DictCursor,
        autocommit=True,
        charset="utf8mb4",
    )

with conn() as c:
    with c.cursor() as cur:
        cur.execute("""
            CREATE TABLE IF NOT EXISTS notes (
                id BIGINT PRIMARY KEY AUTO_INCREMENT,
                body TEXT NOT NULL,
                created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
            )
        """)
        cur.execute("INSERT INTO notes (body) VALUES (%s)", ("hello from replit",))
        cur.execute("SELECT id, body, created_at FROM notes ORDER BY id DESC LIMIT 3")
        print(cur.fetchall())
```

Use parameterized queries (`%s`) like this. Don't build SQL strings with f-strings unless you enjoy SQL injection.

### Connection pooling (do you need it?)

For tiny apps, opening a connection per request is fine. For busier apps, it gets slow and noisy. Cloud SQL also has connection limits, and you can hit them faster than you think.

In Python, you can use a pool (or at least reuse a global connection carefully). If you're using a web framework (Flask/FastAPI), a decent middle ground is:
- create a connection per request
- close it at the end of the request
- add pooling later if you actually need it

Pooling adds complexity. It also fixes real problems. Add it when you see connection churn and latency, not as a hobby.

## Troubleshooting (the usual suspects)

### "Can't connect"

This is almost always:

- Wrong host/user/pass
- Cloud SQL not allowing your inbound IP (Option A)
- You're in the wrong region or hitting a private IP you can't route to
- SSL/TLS requirements (less common, but it happens)
- Proxy not running (Option B)

Checks that actually save time:

- **Confirm the instance IP you're using**
  Cloud SQL - instance - Overview - Public IP address. Copy/paste it. Don't trust your memory.
- **Confirm the user exists and can access the database**
  Try connecting from Cloud Shell or your laptop using the same credentials. If you can't connect from anywhere, Replit won't magically fix it.
- **Authorized networks matches your outbound IP** (Option A)
  If you're using Authorized Networks, confirm the IP Replit uses. If it changed, Cloud SQL will block you and you'll stare at timeouts like it's a personality test.
- **Proxy is listening locally** (Option B)
  If your app connects to `127.0.0.1:3306`, but nothing is listening there, you'll get a fast "connection refused," not a slow timeout. That difference matters.

### Slow queries

Put Cloud SQL in the **same region** as your app. Since Replit is on GCP now, this actually matters. Cross-region latency is real, and it stacks up fast if your app does multiple queries per page load.

Then do the unsexy work:

- add indexes (for the columns you filter/sort on)
- stop doing `SELECT *`
- don't sort giant tables in Python
- don't do N+1 queries (fetching a list, then querying once per item)

Example: if you have a `users` table and you frequently look up by email, add an index:

```sql
CREATE INDEX idx_users_email ON users(email);
```

If you're sorting by `created_at` and filtering by `user_id`, a compound index often helps:

```sql
CREATE INDEX idx_notes_user_created ON notes(user_id, created_at);
```

Indexes speed up reads and slow down writes a bit. For most app tables, it's worth it.

### "I hardcoded the password... just for now"

No. Use Secrets. "Just for now" turns into "six months later" with impressive consistency.

Also: if you already committed it once, deleting it from the file doesn't delete it from Git history. Rotate the password. Yes, even if it's annoying. Especially if it's annoying.

## Extra guides you might want next

- Cloud SQL Auth Proxy docs: https://cloud.google.com/sql/docs/mysql/sql-proxy
- If you're doing this with other engines, I've got similar guides for AWS RDS MySQL, AWS RDS MariaDB, and GCP PostgreSQL (same idea, different flavor of networking pain).

If you get stuck, assume it's networking or credentials first. I've used this setup on a few projects, and the failures are usually boring. MySQL is usually innocent. Not always, but usually.

-Sethers


```json
{"@context": "https://schema.org", "@type": "BlogPosting", "headline": "Connect Replit to GCP Cloud SQL MySQL: Public IP vs Auth Proxy Setup Guide", "datePublished": "2026-01-14T05:26:47", "dateModified": "2026-03-22T21:11:07", "image": {"@type": "ImageObject", "url": "https://cdn.blackseoanalyzer.com/blog-images/connect-your-replit-app-to-a-gcp-mysql-cloud-database-social.jpg", "width": 400, "height": 400}, "url": "https://www.sethserver.com/programming/connect-your-replit-app-to-a-gcp-mysql-cloud-database.html", "author": {"@type": "Person", "name": "Seth Black", "url": "https://www.linkedin.com/in/seth-black-tx"}, "publisher": {"@type": "Organization", "name": "Sethserver", "logo": {"@type": "ImageObject", "url": "https://www.sethserver.com/static/images/PaperAirplane512.png"}}, "description": "Learn how to connect a Replit Python app to Google Cloud SQL MySQL securely and efficiently. This practical guide covers two connection methods (public IP with authorized networks vs Cloud SQL Auth Proxy), credential management with Replit Secrets, complete Python code examples with pymysql, and troubleshooting tips for common networking and latency issues. Includes SQL setup, connection pooling considerations, and security best practices for avoiding hardcoded passwords.", "articleSection": "Technology"}
{"@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{"@type": "ListItem", "position": 1, "name": "sethserver", "item": "https://www.sethserver.com/"}, {"@type": "ListItem", "position": 2, "name": "Programming", "item": "https://www.sethserver.com/programming/"}, {"@type": "ListItem", "position": 3, "name": "Connect Replit to GCP Cloud SQL MySQL: Public IP vs Auth Proxy Setup Guide", "item": "https://www.sethserver.com/programming/connect-your-replit-app-to-a-gcp-mysql-cloud-database.html"}]}
```
