A local Google Cloud emulator that enforces IAM. It runs Cloud Storage, Pub/Sub, Firestore, Spanner, BigQuery, Cloud SQL, Cloud Run, Cloud Logging, Vertex AI, and more on your machine. Use the real client libraries and Terraform, and catch missing IAM grants before you deploy: a call your identity isn't granted is denied locally, with the status code real GCP returned in the recorded scenarios.
gcp-local issues local credentials and evaluates IAM policies on every request. For every service, selected parts of its behavior (status codes, allow/deny decisions, and chosen response fields) are checked against scenarios recorded from real Google Cloud; what that proves and doesn't.
Status: pre-1.0. Nothing is published to PyPI or a container registry yet; run it from source. Each service implements part of its API, enough for common workflows but not every method: Storage serves 25 of its 88 methods (coverage).
You need Docker with Compose and Python 3.11+. No Google Cloud account or credentials are used.
git clone https://github.com/evanmarshall/gcp-local.git
cd gcp-local
python3 scripts/dev/local_env.py up # builds and starts the environment
python3 scripts/dev/local_env.py env # prints the endpoint, project, and credential fileHTTP is on http://127.0.0.1:4566 and gRPC on 127.0.0.1:4567 (TLS on 127.0.0.1:4584). The project is gcp-local-test, and a generated developer identity, serviceAccount:developer@gcp-local-test.iam.gserviceaccount.com (GCP_LOCAL_PRINCIPAL in env), holds explicit IAM grants. reset wipes all data, down stops everything, and logs shows recent output.
Point a client library at it with the generated credential file:
import google.auth
from google.cloud import storage
credentials, _ = google.auth.load_credentials_from_file(
".gcp-local/environment/identity/adc.json",
scopes=["https://www.googleapis.com/auth/cloud-platform"],
)
client = storage.Client(
project="gcp-local-test",
credentials=credentials,
client_options={"api_endpoint": "http://127.0.0.1:4566"},
)
bucket = client.bucket("my-bucket")
# Uniform bucket-level access is required. The other two settings are
# optional; without them the bucket gets GCP's defaults.
bucket.iam_configuration.uniform_bucket_level_access_enabled = True
bucket.iam_configuration.public_access_prevention = "enforced"
bucket.soft_delete_policy.retention_duration_seconds = 0
client.create_bucket(bucket, location="us-central1")
bucket.blob("hello.txt").upload_from_string("Hello from local GCP.")More examples are in examples/: Pub/Sub over gRPC, and a Terraform-provisioned Pub/Sub → Cloud Run → Cloud Storage workflow. The quickstart covers Terraform credentials, multiple environments, and configuration. Client setup has the endpoint and transport settings for Go, Python, Node.js, gcloud, and rclone.
Service coverage lists methods served, data-plane support, recordings, and Terraform tests per service.
| Service | Backend | Application use |
|---|---|---|
| Cloud Storage | fake-gcs-server | Objects: read, write, list, delete, copy, metadata updates; resumable uploads; V4 signed URLs; versioning, lifecycle rules, retention and holds, Pub/Sub notifications |
| Pub/Sub | Official Google emulator | Publish, pull, streaming pull, push, ordering, filters, dead-letter topics, retry backoff, exactly-once, snapshots and seek, Avro and Protocol Buffer schemas |
| Secret Manager | gcp-local | Secret versions and access over REST and gRPC; rotation notifications, expiration, aliases, and delayed destruction |
| Cloud KMS | gcp-local | Symmetric encrypt and decrypt, asymmetric sign and decrypt, HMAC, and scheduled rotation over REST and gRPC |
| Cloud Tasks | gcp-local | HTTP dispatch with OIDC and retries over REST and gRPC |
| Cloud Scheduler | gcp-local | Cron jobs that call HTTP targets or publish to Pub/Sub, over REST and gRPC |
| Cloud Logging | gcp-local | Write, filter, and delete log entries over REST and gRPC |
| Vertex AI | Deterministic stub or Ollama | Generate, stream, count tokens, embed; answers are not from Gemini |
| Firestore | Official Google emulator | Documents, subcollections, queries, transactions, batch writes, listeners |
| BigQuery | GoogleSQL engine | GoogleSQL queries, DML, DDL, parameters, streaming inserts, partitioned and clustered tables, dataset access and authorized views, dbt-bigquery models and tests (not seeds) |
| Cloud SQL for PostgreSQL and MySQL | Real PostgreSQL or MySQL | PostgreSQL and MySQL clients on 127.0.0.1 with a user password, TLS, and client certificates; backups and restore |
| Memorystore for Redis | Real Redis | Redis clients on the reported port, with AUTH |
| Cloud Run | Local processes or Docker | Your own images with --cloud-run-docker; ID-token invocation; traffic splits, tag URLs, internal ingress; admin API over REST and gRPC |
| Spanner | Official Google emulator | SQL queries, DML, reads, mutations, transactions |
| Bigtable | Official Google emulator code | Row reads, scans, mutations over gRPC; not recorded |
| Artifact Registry | gcp-local | Repositories only; no push or pull |
| IAM and project IAM | gcp-local | Service accounts and policies; impersonation (generateAccessToken, generateIdToken, signJwt, signBlob); deny policies |
| Resource Manager | gcp-local | Folders, project moves, and organization and folder IAM inherited by everything under them |
| Organization Policy | gcp-local | Policies on organizations, folders, and projects; 6 constraints enforced on Storage and IAM requests |
| VPC networking | gcp-local | Connectivity verdicts; no real traffic |
IAM allow policies use predefined roles recorded from Google Cloud. IAM conditions are recorded for Storage, Cloud SQL, Memorystore, Secret Manager, and Cloud KMS; on other services a condition that reads resource.* grants nothing. IAM v2 deny policies are served and enforced on every service, recorded on a project (deny policies); principal access boundaries are local-only and not recorded (IAM). Organization policies are enforced for 6 constraints; VPC Service Controls are not enforced (Organization Policy).
A single gateway routes requests to per-service providers, following LocalStack's architecture. Providers authenticate and authorize before touching state, and reuse real backends (Google's emulators, PostgreSQL, MySQL, Redis) where they exist. gRPC stubs are generated from Google's published protobuf descriptors. REST routes are generated from Google's discovery documents for the Storage JSON API, Memorystore for Redis, Cloud SQL, and Cloud Scheduler, and from route tables written by hand for the other services (api/README.md, design, LocalStack comparison).
Scenarios are recorded once against a sandbox Google Cloud project and replayed against the emulator in CI, alongside Terraform acceptance cases run with the pinned google provider (3 extracted from the provider's upstream tests, the rest written locally; Terraform) and client-library tests (validation).
- Unsupported methods and options return
UNIMPLEMENTED. - State is in memory;
resetanddowndiscard it. - Storage buckets need uniform bucket-level access. ACLs, requester pays, encryption keys, and compose are not modeled, and deleted buckets are not soft-deleted (Storage).
- Packaged Cloud Run runs your own images only with
gcp-local --cloud-run-docker up, which mounts the Docker socket (root-equivalent on the host). Revisions have no internet access (Cloud Run). - Redis and Cloud SQL listen on
127.0.0.1ports 4568-4583 by default, not 6379, 5432, or 3306; read the port from the API. Redis doesn't accept TLS; Cloud SQL serves TLS with the instance's own CA (Cloud SQL). - The Spanner emulator runs on Linux and in the container image, not natively on macOS.
- Networking verdicts are analytical, not packet routing.
- Vertex AI answers come from a stub or local Ollama, not Gemini.
See CONTRIBUTING.md and the issue tracker.
MIT. See LICENSE. Vendored Google API discovery documents under api/discovery/ keep their Apache-2.0 license.