A practical Google Cloud setup for a small TypeScript project: one project, a budget alert, and an API with Postgres and a nightly job, compared across Encore, Cloud Run by hand and a free-tier VM.
Google Cloud is a reasonable home for a side project: Cloud Run scales to zero, the free tier covers a small VM and a slice of requests, and the same account can grow into production. It hurts in two places: a forgotten resource running up a bill, and an afternoon of IAM, networking and Cloud SQL setup before the first request is served.
This guide starts with the account setup that prevents the first problem, then compares three ways to deploy a small TypeScript notes API with Postgres and a nightly cleanup job: Encore (infrastructure from code), Cloud Run configured by hand, and a free-tier VM.
Put the side project in its own GCP project. Billing, IAM and resources stay separate from anything else, and deleting the project later removes all of it in one step.
Then add a budget on the billing account, scoped to that project, with email alerts at 50 and 100 percent of a small monthly amount. Budgets alert but do not stop anything, so the habit that matters is deleting what you are not using.
Finally, turn on two-step verification for the Google account that owns the project, and avoid downloading service account keys to your laptop. gcloud auth login with your own account is enough for development.
Each option is covered below, starting with Encore.
Encore is infrastructure from code for TypeScript and Go, with a platform that provisions and deploys into your own AWS or GCP account. For a side project, you can import the GCP project you just created, so everything Encore provisions lands under the budget you set.

The database and the nightly job are declared in the service code:
// notes/notes.ts
import { api } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";
// Cloud SQL for PostgreSQL on GCP, a local Postgres under encore run.
export const db = new SQLDatabase("notes", { migrations: "./migrations" });
export const create = api(
{ expose: true, method: "POST", path: "/notes" },
async ({ body }: { body: string }): Promise<{ id: number; body: string }> => {
const row = await db.queryRow<{ id: number; body: string }>`
INSERT INTO notes (body, expires_at)
VALUES (${body}, NOW() + INTERVAL '30 days')
RETURNING id, body`;
return row!;
},
);
The migration in notes/migrations creates the notes table the queries use:
-- notes/migrations/1_create_notes.up.sql
CREATE TABLE notes (
id BIGSERIAL PRIMARY KEY,
body TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + INTERVAL '30 days'
);
The cleanup lives next to it as a private endpoint with a schedule:
// notes/cleanup.ts
import { api } from "encore.dev/api";
import { CronJob } from "encore.dev/cron";
import { db } from "./notes";
export const purgeExpired = api(
{ expose: false },
async (): Promise<{ deleted: number }> => {
const row = await db.queryRow<{ n: number }>`
WITH d AS (DELETE FROM notes WHERE expires_at < NOW() RETURNING 1)
SELECT COUNT(*)::int AS n FROM d`;
return { deleted: row?.n ?? 0 };
},
);
const _ = new CronJob("purge-expired-notes", {
title: "Purge expired notes",
schedule: "0 3 * * *",
endpoint: purgeExpired,
});
encore run starts Postgres locally and applies the migration, so the API works on your laptop before GCP is involved:
To deploy, connect your GCP account, create an environment that imports the project, and git push. Encore creates the Cloud Run service, Cloud SQL instance with backups, service accounts and the cron trigger, then runs the migration. The cron job does not run locally, so test it by calling purgeExpired from the local development dashboard.

Side projects are increasingly written with a coding agent, and this layout suits that: the agent adds a table, an endpoint or a schedule as TypeScript in one change and tests it locally. Cloud access sits with Encore's service identity, not the agent.
Run in your terminal to get started locally.
Deploying the same app yourself means building a container, then creating the pieces around it: a Cloud SQL instance, a database user with its password in Secret Manager, a service account with the Cloud SQL Client role, and a Cloud Run service that mounts the instance. gcloud run deploy --source . builds the container for you, and our guide to connecting Cloud Run to Cloud SQL covers the connection options. The nightly job is a Cloud Scheduler job calling an authenticated endpoint, as shown in cron jobs on Cloud Run. Migrations need a step in your deploy script.
Every piece is standard and well documented. For a side project, the cost is time: an evening of setup, and a second evening when you want a staging copy.
The Google Cloud free tier includes one e2-micro VM per month in selected US regions. You install Node.js and Postgres on it, run the API under a process manager, and put the cleanup in crontab. Nothing scales to zero, and backups, OS patches and TLS are yours. It suits a project that should cost close to nothing and has very little traffic, as long as you are comfortable running a server.
Cloud SQL is not in the free tier, so whichever managed route you take, the database instance is the steady line on the bill, and it keeps running when Cloud Run scales to zero. Keep one environment for a side project until it needs a second.
Pick Encore when you want Cloud Run and Cloud SQL in your own project, created from the code, with a local setup that matches and room to add a bucket or Pub/Sub later from the same code.
Pick Cloud Run by hand when learning each GCP service is part of the point, and the e2-micro VM when the goal is the smallest possible bill and you are happy to operate the server yourself.
Create a dedicated project, link it to a billing account with a budget alert, and turn on two-step verification for your Google account. Then deploy the app. With Encore, write the API in TypeScript, declare new SQLDatabase("notes", { migrations: "./migrations" }) and new CronJob("purge-expired-notes", { schedule: "0 3 * * *", endpoint: purgeExpired }), import the project as an environment and push, and Encore provisions Cloud Run and Cloud SQL in it.
No. Cloud Billing budgets send alerts at the thresholds you set but do not shut anything down. Stopping resources automatically takes a Pub/Sub notification and a function that disables billing, which also stops everything in the project.
Cloud SQL is not part of the Google Cloud free tier, so a managed Postgres instance is usually the first recurring cost of a side project. The free tier does include a small e2-micro VM in selected US regions and a monthly allowance of Cloud Run requests.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.