# gcloud CLI Cheat Sheet — Databases: Cloud SQL, Firestore & Memorystore

> **Tool:** Google Cloud CLI (gcloud)
> **Category:** Cloud CLIs
> **Verified against:** Google Cloud SDK 553.0.0, flags verified via `gcloud sql instances create --help`,
> `gcloud sql connect --help`, `gcloud sql users create --help`, `gcloud sql databases create --help`,
> `gcloud sql backups create --help`, `gcloud sql export sql --help`, `gcloud sql instances patch --help`,
> `gcloud firestore databases create --help`, and `gcloud redis instances create --help` run locally,
> 2026-09-17
> **Official docs:** https://cloud.google.com/sdk/gcloud/reference/sql, https://cloud.google.com/sdk/gcloud/reference/firestore, and https://cloud.google.com/sdk/gcloud/reference/redis

## What it is and where it fits 🎯

Cloud SQL is GCP's managed relational database service, MySQL, PostgreSQL, or SQL Server, running on
infrastructure Google patches and backs up for you, the direct analogue of AWS RDS or Azure SQL Database.
Firestore is GCP's serverless NoSQL document database, closer to DynamoDB than to anything relational.
Memorystore for Redis is a managed in-memory cache/data-store layer, most often sitting in front of one of
the other two. This page assumes the project/auth basics from page `01` and the VPC networking primitives
(private IP, VPC peering) from page `03`, since Cloud SQL's private-IP connectivity pattern depends directly
on them.

## Core concepts: how an application actually reaches Cloud SQL

```mermaid
flowchart TD
    App["Application<br/>(GKE pod / Cloud Run / laptop)"] -->|"IAM-authenticated,<br/>no static password"| Proxy["Cloud SQL Auth Proxy<br/>(sidecar or local binary)"]
    Proxy -->|"encrypted tunnel"| Instance["Cloud SQL instance"]
    App -.->|"direct, if on the same VPC"| PrivateIP["Private IP<br/>(VPC peering required)"]
    PrivateIP --> Instance
    App -.->|"discouraged"| PublicIP["Public IP + authorized networks"]
    PublicIP --> Instance

    classDef ok fill:#e5f5ea,stroke:#1f8a4c,color:#10161c
    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef crit fill:#fbe8e6,stroke:#b3261e,color:#10161c
    class Proxy ok
    class PrivateIP info
    class PublicIP crit
```

Three real connection paths exist, and they're not interchangeable defaults. The **Cloud SQL Auth Proxy**
(a separate binary, `cloud-sql-proxy`, not a `gcloud` subcommand) authenticates via IAM and encrypts the
connection without you ever managing a certificate or authorized-network entry, the current recommended
default for anything running on GCP compute. A **private IP** on the same VPC skips the proxy but requires
the VPC peering this site's page `03` covers, set up once per network. A **public IP with authorized
networks** is the path `gcloud sql connect` below uses for quick interactive access, and the one to avoid
for anything long-running or automated.

## Creating a Cloud SQL instance

```bash
gcloud sql instances create my-instance \
  --database-version=POSTGRES_16 \
  --tier=db-custom-2-8192 --region=us-central1 \
  --storage-type=SSD --storage-size=50GB --storage-auto-increase \
  --backup --backup-start-time=03:00 \
  --availability-type=REGIONAL \
  --require-ssl --deletion-protection
```

- **`--tier`** picks the machine shape; `db-custom-<vCPUs>-<MB-RAM>` gives you an exact custom shape, or use
  one of the predefined `db-f1-micro`/`db-g1-small`/`db-n1-standard-*` tiers for a smaller/simpler instance.
- **`--availability-type=REGIONAL`** provisions a synchronous standby in a second zone within the region,
  GCP's equivalent of AWS RDS Multi-AZ, `ZONAL` (the default) has no automatic failover.
- **`--deletion-protection`** blocks `gcloud sql instances delete` outright until explicitly disabled with
  `gcloud sql instances patch my-instance --no-deletion-protection` first, a genuinely useful guardrail for
  a production instance that shouldn't be one accidental command away from gone.
- **`--storage-auto-increase`** grows storage automatically as it fills rather than the instance going
  read-only at capacity, the safer default for anything you're not actively capacity-planning by hand.

## Listing, describing, and connecting

```bash
gcloud sql instances list
gcloud sql instances describe my-instance
gcloud sql instances describe my-instance --format="value(ipAddresses[0].ipAddress)"

gcloud sql connect my-instance --user=postgres --database=my-db   # interactive psql/mysql shell
```

`gcloud sql connect` is genuinely convenient for a one-off interactive session, it temporarily adds your
current IP to the instance's authorized networks, opens an interactive client, then removes the temporary
entry when you exit. It is not the pattern for an application's connection string, an app should use the
Cloud SQL Auth Proxy or private IP shown in the diagram above, both of which avoid touching authorized
networks at all.

> [!NOTE]
> `gcloud sql connect` isn't supported for an instance with only a private IP and no public IP assigned,
> the same private-only instance an application should be reaching via the Auth Proxy or VPC peering. For
> a quick interactive session against a private-only instance, `gcloud beta sql connect` routes through the
> Cloud SQL Auth Proxy automatically instead of requiring a public IP at all.

## Databases and users

```bash
gcloud sql databases create my-app-db --instance=my-instance --charset=UTF8
gcloud sql databases list --instance=my-instance
gcloud sql databases delete my-app-db --instance=my-instance

gcloud sql users create app-user --instance=my-instance --password=<generated-secret>
gcloud sql users set-password app-user --instance=my-instance --password=<new-secret>
gcloud sql users list --instance=my-instance
gcloud sql users delete app-user --instance=my-instance
```

A Cloud SQL "instance" is the managed server; "databases" and "users" underneath it map directly onto the
underlying engine's own concepts (a MySQL/Postgres database and role), `gcloud sql databases`/`gcloud sql
users` are just a remote-management wrapper around `CREATE DATABASE`/`CREATE USER` so you don't need a live
SQL connection just to provision a new schema or credential.

```bash
gcloud sql instances patch my-instance --database-flags=cloudsql.iam_authentication=on
gcloud sql users create ci-deployer@my-project-id.iam.gserviceaccount.com \
  --instance=my-instance --type=cloud_iam_service_account
```

> [!TIP]
> For Postgres and MySQL instances, prefer **IAM database authentication** over a static password: enable
> the `cloudsql.iam_authentication` database flag on the instance, then create a user with
> `--type=cloud_iam_user` (a human) or `--type=cloud_iam_service_account` (a workload) instead of a
> password-typed user. The application then authenticates using its own service account's IAM identity, no
> password stored anywhere, consistent with the service-account-over-key-file preference on page `01`.

## Backups, exports, and restores

```bash
gcloud sql backups create --instance=my-instance --description="pre-migration snapshot"
gcloud sql backups list --instance=my-instance
gcloud sql backups restore <backup-id> --restore-instance=my-instance

gcloud sql export sql my-instance gs://my-bucket/backup.sql --database=my-app-db
gcloud sql import sql my-instance gs://my-bucket/backup.sql --database=my-app-db
```

Automated backups (configured via `--backup`/`--backup-start-time` at instance creation) run on a schedule
and are what `--enable-point-in-time-recovery` builds on for restoring to an arbitrary timestamp, not just
a backup boundary; `gcloud sql backups create` is an on-demand one, worth running immediately before a risky
schema migration rather than trusting the nightly window happened to land beforehand. `export`/`import`
round-trip through a Cloud Storage bucket as a portable SQL dump, useful for cloning data into a staging
instance or migrating between Cloud SQL and a self-managed database, at the cost of being far slower than a
native backup/restore for anything beyond a modest dataset size.

## Read replicas and high availability

```bash
gcloud sql instances create my-instance-replica \
  --master-instance-name=my-instance \
  --region=us-east1 --tier=db-custom-2-8192
```

A read replica is created as its own named instance pointing back at a `--master-instance-name`, and can
live in a different region entirely, useful for serving read traffic closer to a secondary user population,
separately from the `--availability-type=REGIONAL` failover standby shown earlier, which exists purely for
availability and isn't a readable, independent endpoint the way a replica is.

## Restricting network access

```bash
gcloud sql instances patch my-instance --authorized-networks=203.0.113.0/24
gcloud sql instances patch my-instance --clear-authorized-networks

gcloud sql instances patch my-instance --no-assign-ip --network=projects/my-project/global/networks/my-network
```

`--authorized-networks` is CIDR-based allow-listing for the public-IP path, the same shape as a GCE firewall
rule's `--source-ranges`, appropriate only when public IP access is genuinely required (a third-party SaaS
tool that can't reach a private VPC, say). `--no-assign-ip` removes the public IP entirely, the instance is
then reachable only via the private-IP path shown in the core-concepts diagram, requiring the referenced
VPC to already have Cloud SQL's private services access configured.

## Firestore: creating a database and managing indexes

```bash
gcloud firestore databases create --location=nam5 --type=firestore-native
gcloud firestore databases list
gcloud firestore databases describe --database="(default)"

gcloud firestore indexes composite create \
  --collection-group=orders --field-config field-path=customerId,order=ascending \
  --field-config field-path=createdAt,order=descending
gcloud firestore indexes composite list
```

Firestore's `--location` is a multi-region or region identifier chosen once at database creation and
**cannot be changed afterward**, the same "get it right the first time" constraint GKE's Autopilot-vs-
Standard choice has on page `02`. `--type=firestore-native` is the modern document-model API; the legacy
`datastore-mode` still exists for projects migrating off the older Datastore API, new projects should
default to native mode. Composite indexes are required for any query that filters or sorts on more than one
field, Firestore rejects such a query outright at request time until the matching index exists, `gcloud
firestore indexes composite create` is how you provision one outside the Console, useful for keeping index
definitions in version control alongside the application code that needs them.

## Memorystore for Redis

```bash
gcloud redis instances create my-cache \
  --region=us-central1 --tier=standard --size=5 \
  --redis-version=redis_7_2 --network=my-network \
  --enable-auth --transit-encryption-mode=SERVER_AUTHENTICATION

gcloud redis instances describe my-cache --region=us-central1 --format="value(host,port)"
gcloud redis instances list --region=us-central1
```

`--tier=standard` provisions a replicated, auto-failover instance (GCP's equivalent of ElastiCache's
Multi-AZ Redis); `basic` is a single node with no failover and no SLA, appropriate only for a cache whose
complete, silent loss is genuinely tolerable. Memorystore is reachable only via private IP on the specified
VPC by design, there's no public-IP option at all, since an unauthenticated Redis endpoint exposed to the
internet is a well-known, actively-scanned-for misconfiguration; `--enable-auth` adds an application-level
AUTH token on top of that network isolation for defense in depth.

## Real-world scenario: zero-downtime major-version upgrade rehearsal

A team needs to validate a PostgreSQL 14 to 16 upgrade against production-shaped data before touching the
real instance:

```bash
gcloud sql instances create prod-pg16-rehearsal \
  --database-version=POSTGRES_16 --tier=db-custom-4-16384 --region=us-central1

gcloud sql export sql prod-instance gs://my-bucket/prod-snapshot.sql --database=app_db
gcloud sql import sql prod-pg16-rehearsal gs://my-bucket/prod-snapshot.sql --database=app_db

# Run the application's real migration + smoke test suite against prod-pg16-rehearsal here

gcloud sql instances delete prod-pg16-rehearsal --quiet   # tear down once validated
```

Exporting and importing into a fresh instance on the target version, rather than upgrading the production
instance directly first, turns a one-way risky operation into a disposable rehearsal, the rehearsal instance
costs money only for the hours it exists and can be deleted the moment the migration is validated or found
to need more work.

## Real-world scenario: locking a Cloud SQL instance down to private IP only

A security review flagged a Cloud SQL instance still reachable on a public IP months after the application
moved onto GKE with private-IP connectivity configured:

- [ ] Confirm every consumer has actually migrated off the public IP (check application connection strings
      and any `--authorized-networks` entries still in use) before touching the instance
- [ ] Run `gcloud sql instances patch my-instance --no-assign-ip` to remove the public IP entirely
- [ ] Confirm private-IP connectivity from a representative pod: `gcloud sql instances describe my-instance
      --format="value(ipAddresses)"` should now show only a `PRIVATE` type entry
- [ ] Update any monitoring/alerting that was keyed to the old public IP address

> [!WARNING]
> Removing the public IP on an instance still being reached by an unmigrated consumer causes an immediate,
> hard connection failure for that consumer, there's no grace period. Confirm every real caller first, not
> just the ones you remember.

## CI/CD integration recipe: running Cloud SQL migrations from GitHub Actions via the Auth Proxy

```yaml
# .github/workflows/db-migrate.yml
name: Run DB migrations
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  migrate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/<project-number>/locations/global/workloadIdentityPools/github-pool/providers/github-provider
          service_account: db-migrator@my-project-id.iam.gserviceaccount.com
      - uses: mattes/gce-cloudsql-proxy-action@v1  # or download cloud-sql-proxy directly
        with:
          instance: my-project-id:us-central1:my-instance
      - run: npm run migrate -- --host=127.0.0.1 --port=5432
```

The Auth Proxy step authenticates via the same Workload Identity Federation-issued token the earlier `auth`
step already obtained, no separate database password lives in CI secrets at all, only IAM permissions on
the `db-migrator` service account control what the pipeline can reach.

## Common pitfalls

- **Reaching for `gcloud sql connect` from application code or a script.** It's built for an interactive
  human session, temporarily mutating authorized networks; use the Auth Proxy or private IP instead.
- **Expecting Firestore's `--location` to be changeable after creation.** It isn't, plan the location before
  the first `firestore databases create`, not after data is already flowing in.
- **Querying Firestore on two+ fields with no matching composite index provisioned.** The query fails
  outright at request time rather than running slowly, provision the index ahead of shipping the query.
- **Deploying Memorystore expecting a public endpoint.** There isn't one by design; the application must be
  on the same VPC (directly or via peering/Serverless VPC Access) to reach it at all.
- **Skipping `--deletion-protection` on anything that holds real data.** It costs nothing and blocks exactly
  one class of accident, an errant `gcloud sql instances delete` against the wrong instance name.

## Exit codes

`0` success, non-zero on any API/validation error, a long-running operation like `sql instances create` that
times out client-side while the instance is still provisioning in the background still exits non-zero even
though the instance may finish successfully moments later; `gcloud sql operations list --instance=my-instance`
confirms the operation's real server-side status rather than trusting only the triggering command's exit code.

## When to reach for something else

For a workload that's genuinely serverless-shaped end to end and doesn't need SQL joins or transactions
beyond a single document, Firestore (or, for very simple key-value needs, Memorystore alone) avoids managing
an instance's sizing and maintenance windows entirely. For declarative, reviewable database provisioning
across environments, prefer Terraform's `google_sql_database_instance`/`google_firestore_database`/
`google_redis_instance` resources over a growing shell script of `gcloud sql`/`gcloud firestore`/`gcloud
redis` calls, consistent with the IaC guidance on pages `01`-`03`.
