Zeabur to Google Cloud Run Without a Dockerfile: Steps We Actually Deployed

Backends deployed on Zeabur straight from GitHub, with no Dockerfile, can move to Cloud Run using a source deploy. We deployed one for real, and list the secret-permission error and its fix.

Key points

  • No Dockerfile needed: deploying with the --source flag uses buildpacks to detect the language.
  • Put secrets in Secret Manager and mount them with --set-secrets; the service account needs the Secret Manager Secret Accessor role.
  • Cloud Run's filesystem is temporary; store uploads in Cloud Storage or R2.
  • Update your chatbot's webhook URL (e.g. in LINE Developers) after moving.
How was this verified? (3 tested, 3 from official docs)
Tested ourselves
  • A Node service with no Dockerfile, deployed with a source deploy to asia-east1; the URL responded (deleted after the test)
  • --set-env-vars for environment variables and --set-secrets for a Secret Manager value; the app could read both
  • The error when the service account cannot read a secret, and a successful redeploy after granting access
Written from official docs, not run ourselves
  • Which APIs to enable and roles to grant in a brand-new GCP project: we tested in an existing project; this part follows Google's official docs
  • LINE Developers console steps: from our own experience running LINE bots, not redone in this test
  • Custom domains: not tested; follow Google's official docs
Contents (8 sections)
  1. Who this is for
  2. Step 0: Prepare a new project (per the official docs)
  3. Step 1: Make sure the app listens on PORT
  4. Step 2: Deploy
  5. Step 3: Put secrets in Secret Manager, not in environment variables
  6. Step 4: The database
  7. Step 5: Update your LINE bot’s webhook URL
  8. Step 6: Custom domains

Who this is for

  • Backends deployed on Zeabur from GitHub: Node.js, Python or Go APIs, or LINE and Discord bots
  • Low traffic, where you’d like to pay no compute when nobody is using it (Cloud Run scales to zero)
  • A nearby region: asia-east1 is in Taiwan; use asia-northeast1 (Tokyo) or asia-northeast3 (Seoul) if those are closer to your users

Not a good fit: services that need a persistent disk (for example, saving uploads to a local folder). Cloud Run’s filesystem is temporary; files written to it disappear after a redeploy or a scale to zero. Store such files in Cloud Storage or R2, or use a VPS instead (see the n8n post).

Step 0: Prepare a new project (per the official docs)

  1. Install the gcloud CLI and run gcloud init to log in and pick a project. The project must be linked to a billing account
  2. Enable the APIs:
gcloud services enable run.googleapis.com cloudbuild.googleapis.com
  1. For source deploys, Cloud Build uses the Compute Engine default service account by default, and the official docs say to grant it the Cloud Run Builder role:
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
  --member=serviceAccount:[email protected] \
  --role=roles/run.builder

The docs note that the grant takes a couple of minutes to propagate.

Always check which project you’re using. Add --project=YOUR_PROJECT_ID to every gcloud command, or run gcloud config get-value project first. Our own machines have several projects, and the default is often not the one we mean to deploy to.

Step 1: Make sure the app listens on PORT

Both Zeabur and Cloud Run tell the app which port to listen on through the PORT environment variable. If you already used $PORT on Zeabur as its docs recommend, nothing changes. If the port is hard-coded to 3000, change it to:

app.listen(process.env.PORT || 3000);

Step 2: Deploy

Run this in the project root (no Dockerfile needed; Google’s buildpacks detect the language):

gcloud run deploy my-api --source . --region asia-east1 \
  --allow-unauthenticated \
  --set-env-vars "NODE_ENV=production" \
  --project=YOUR_PROJECT_ID

When it finishes it prints Service URL: https://my-api-xxxx.asia-east1.run.app. In our test, a Node service with no Dockerfile responded on the very first deploy.

If the project root has a Dockerfile, Cloud Run uses it instead.

Step 3: Put secrets in Secret Manager, not in environment variables

Anyone who can view the service configuration can see --set-env-vars values. Put database passwords and API keys in Secret Manager:

# Create the secret (printf adds no trailing newline)
printf '%s' 'postgresql://...' | gcloud secrets create database-url --data-file=- --project=YOUR_PROJECT_ID

# Mount it on the service; the app still reads process.env.DATABASE_URL
gcloud run services update my-api --region asia-east1 \
  --set-secrets "DATABASE_URL=database-url:latest" --project=YOUR_PROJECT_ID

The error you will probably hit

If the service account cannot read the secret, the deploy fails:

ERROR: (gcloud.run.deploy) ... Permission denied on secret: projects/.../secrets/database-url/versions/latest
for Revision service account [email protected]. The service account used must be granted
the 'Secret Manager Secret Accessor' role (roles/secretmanager.secretAccessor) at the secret, project or higher level.

The fix is to grant access on that one secret only (the narrowest scope) and redeploy:

gcloud secrets add-iam-policy-binding database-url \
  --member="serviceAccount:THE_SERVICE_ACCOUNT_FROM_THE_ERROR" \
  --role="roles/secretmanager.secretAccessor" --project=YOUR_PROJECT_ID

In our test, the redeploy succeeded immediately after the grant.

Step 4: The database

Cloud Run doesn’t include a database. PostgreSQL can move to a managed service such as Neon or Supabase, or to Google’s Cloud SQL. See the database post.

Step 5: Update your LINE bot’s webhook URL

  1. Open the LINE Developers console and select your channel
  2. On the Messaging API tab, change the Webhook URL to the new Cloud Run URL (plus your app’s path, such as /webhook)
  3. Click Verify to confirm it works

We also recommend replacing the Channel Secret and Access Token while you’re at it; see the security post.

Step 6: Custom domains

There are several ways to put a custom domain on Cloud Run (Cloud Run domain mapping, a load balancer, Firebase Hosting), with different region support and costs; choose using Google’s official docs. We did not test this step. For DNS switch-over tips, see the domains and DNS post.

FAQ

Can a Zeabur project without a Dockerfile be deployed to Cloud Run?

Yes. A source deploy (--source) without a Dockerfile uses Google Cloud buildpacks to detect the language and build the image. We tested with a Node.js service and it worked on the first deploy.

What do I do about Permission denied on secret in Cloud Run?

The service account used by the service cannot read that secret. Grant it roles/secretmanager.secretAccessor (you can scope it to that one secret) and redeploy.