GCP on localhost:4588
A free, MIT-licensed Google Cloud emulator. Give your coding agents a real GCP to build and test against: no project, no billing account, no auth token.
# Install floci-cli, then start floci-gcp curl -fsSL https://floci.io/install.sh | sh floci gcp start eval $(floci gcp env)
24 ms startup · ~13 MiB idle · MIT licensed · no GCP project
Homebrew, Windows and Docker optionsEvery way to run it.
One CLI installs and runs all four emulators: use Homebrew, the install script or PowerShell. Or skip the CLI and run the Docker image.
1 · Install floci CLI
brew install floci-io/floci/floci2 · Start the emulator
floci gcp start3 · Export env vars
eval $(floci gcp env) # exports STORAGE_EMULATOR_HOST, # PUBSUB_EMULATOR_HOST, FIRESTORE_EMULATOR_HOST, # DATASTORE_EMULATOR_HOST, SECRET_MANAGER_EMULATOR_HOST # gcloud ignores those; point it at floci-gcp too # (any token works locally) export CLOUDSDK_API_ENDPOINT_OVERRIDES_STORAGE=http://localhost:4588/ export CLOUDSDK_CORE_PROJECT=floci-local export CLOUDSDK_AUTH_ACCESS_TOKEN=floci
4 · Upload a file
# Create a bucket gcloud storage buckets create gs://my-bucket # Write a file and upload it echo "No billing alert? Must be floci." \ > hello-floci.txt gcloud storage cp hello-floci.txt \ gs://my-bucket/hello-floci.txt # Download it back and read it gcloud storage cp \ gs://my-bucket/hello-floci.txt \ hello-back.txt cat hello-back.txt
Teach your coding agent
# Asks which agents to install it for npx skills add floci-io/floci-cli
The Floci skill teaches Claude Code, Codex, Cursor, Kiro and other coding agents to run the emulators with floci-cli.
Other useful commands
# Check status and server health floci gcp status # List enabled services floci gcp services # View logs floci gcp logs # Stop the emulator floci gcp stop # Export env for selected services only eval $(floci gcp env --service gcs,pubsub)
Start with persistence
floci gcp start --persist ./data # state survives container restarts
Diagnose issues
floci gcp doctor # checks Docker, gcloud, connectivity
Client libraries and gcloud, side by side.
Point your existing GCP client libraries at localhost:4588. No code changes beyond the endpoint.
# pip install google-cloud-storage import os from google.cloud import storage os.environ["STORAGE_EMULATOR_HOST"] = "http://localhost:4588" client = storage.Client(project="floci-local") # create a bucket and upload a file bucket = client.lookup_bucket("demo-bucket") or client.create_bucket("demo-bucket") blob = bucket.blob("data/hello.txt") blob.upload_from_string("hello from floci-gcp!") # list objects in the bucket for b in client.list_blobs("demo-bucket"): print(b.name, b.size) # download and read back print(blob.download_as_text())
# point gcloud at floci-gcp (any token works locally) export CLOUDSDK_API_ENDPOINT_OVERRIDES_STORAGE=http://localhost:4588/ export CLOUDSDK_CORE_PROJECT=floci-local export CLOUDSDK_AUTH_ACCESS_TOKEN=floci # create a bucket gcloud storage buckets describe gs://demo-bucket >/dev/null 2>&1 \ || gcloud storage buckets create gs://demo-bucket # upload a file echo "hello from floci-gcp!" > hello.txt gcloud storage cp hello.txt gs://demo-bucket/hello.txt # list objects gcloud storage ls gs://demo-bucket # download it back gcloud storage cp gs://demo-bucket/hello.txt -
# pip install google-cloud-pubsub import os from google.api_core.exceptions import AlreadyExists from google.cloud import pubsub_v1 os.environ["PUBSUB_EMULATOR_HOST"] = "localhost:4588" project = "floci-local" publisher = pubsub_v1.PublisherClient() subscriber = pubsub_v1.SubscriberClient() # create topic and subscription topic = publisher.topic_path(project, "my-topic") sub = subscriber.subscription_path(project, "my-sub") try: publisher.create_topic(request={"name": topic}) subscriber.create_subscription( request={"name": sub, "topic": topic} ) except AlreadyExists: pass # already created on an earlier run # publish a message future = publisher.publish(topic, b"hello from floci-gcp!") print("Published: " + str(future.result())) # pull and acknowledge resp = subscriber.pull( request={"subscription": sub, "max_messages": 1} ) for msg in resp.received_messages: print(msg.message.data.decode()) subscriber.acknowledge( request={"subscription": sub, "ack_ids": [msg.ack_id]} )
// npm install @google-cloud/pubsub process.env.PUBSUB_EMULATOR_HOST = 'localhost:4588'; const { PubSub } = require('@google-cloud/pubsub'); const pubsub = new PubSub({ projectId: 'floci-local' }); async function run() { const [topic] = await pubsub .topic('my-topic').get({ autoCreate: true }); const [sub] = await topic .subscription('my-sub').get({ autoCreate: true }); // publish a message const msgId = await topic.publishMessage({ data: Buffer.from('hello from floci-gcp!'), }); console.log('Published: ' + msgId); // receive one message sub.on('message', msg => { console.log(msg.data.toString()); msg.ack(); sub.removeAllListeners(); }); } run();
# pip install google-cloud-firestore import os from google.cloud import firestore os.environ["FIRESTORE_EMULATOR_HOST"] = "localhost:4588" db = firestore.Client(project="floci-local") # write a document ref = db.collection("users").document("alice") ref.set({"name": "Alice", "score": 42}) # read it back doc = ref.get() print(doc.to_dict()) # query with a filter for doc in ( db.collection("users") .where("score", ">=", 40) .stream() ): print(doc.id, doc.to_dict())
// npm install @google-cloud/firestore process.env.FIRESTORE_EMULATOR_HOST = 'localhost:4588'; const { Firestore } = require('@google-cloud/firestore'); const db = new Firestore({ projectId: 'floci-local' }); async function run() { // write a document const ref = db.collection('users').doc('alice'); await ref.set({ name: 'Alice', score: 42, active: true }); // read it back const doc = await ref.get(); console.log(doc.data()); // query with a filter const snap = await db.collection('users') .where('score', '>=', 40).get(); snap.forEach(d => console.log(d.id, d.data())); } run();
# pip install google-cloud-secret-manager import grpc from google.api_core.exceptions import AlreadyExists from google.cloud import secretmanager from google.cloud.secretmanager_v1.services.secret_manager_service.transports.grpc import ( SecretManagerServiceGrpcTransport, ) # the Python client ignores *_EMULATOR_HOST, so point it at floci-gcp over plain gRPC transport = SecretManagerServiceGrpcTransport( channel=grpc.insecure_channel("localhost:4588") ) client = secretmanager.SecretManagerServiceClient(transport=transport) parent = "projects/floci-local" # create the secret (skipped if it exists) name = parent + "/secrets/db-password" try: client.create_secret(request={ "parent": parent, "secret_id": "db-password", "secret": {"replication": {"automatic": {}}}, }) except AlreadyExists: pass # store the value client.add_secret_version(request={ "parent": name, "payload": {"data": b"super-secret-value"}, }) # read the latest version version = name + "/versions/latest" resp = client.access_secret_version(request={"name": version}) print(resp.payload.data.decode())
# point gcloud at floci-gcp (any token works locally) export CLOUDSDK_API_ENDPOINT_OVERRIDES_SECRETMANAGER=http://localhost:4588/ export CLOUDSDK_CORE_PROJECT=floci-local export CLOUDSDK_AUTH_ACCESS_TOKEN=floci # gcloud can't add secret versions on floci-gcp yet: store them from a # client library (left), then read and list them here gcloud secrets list # read the latest version gcloud secrets versions access latest \ --secret db-password
27 GCP services. All of them free.
No project ID, no service account, no billing account. Every service fully unlocked.
Runs a Docker container
The client libraries you already use.
Point the official Google Cloud client libraries and gcloud at localhost:4588. No project, no service account key.
Emulator variables, set for you
floci gcp env exports STORAGE, PUBSUB, FIRESTORE, DATASTORE and SECRET_MANAGER *_EMULATOR_HOST variables for the libraries that read them.
gcloud included
gcloud ignores those variables, so one endpoint override and any access token point it at floci-gcp instead.
Real containers where it matters
Cloud Run, Cloud SQL, GKE (on k3s) and Managed Kafka (on Redpanda) run real containers through Docker.
Native-image speed
Starts in 24 ms, idles at ~13 MiB and works offline, so CI never waits on the network.
Google Cloud, without the project.
MIT licensed and built for developer workflows. No GCP project, no billing account, no auth token. Just floci-cli and localhost.
- license
- MIT
- real credentials
- none
- cloud account
- none
- cost
- $0