<@ULVA73B9P> I keep getting this error when trying...
# ask-marvin
d
@Marvin I keep getting this error when trying to deploy my prefect image:
Copy code
google.auth.exceptions.DefaultCredentialsError: File /opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json was not found.
The json file is in the root where my prefect_dev.yaml file is, and my yaml looks like this:
Copy code
name: atl-data-pipeline
prefect-version: 2.20.2

# build section allows you to manage and build docker images
build:
- prefect_docker.deployments.steps.build_docker_image:
    id: build_image
    requires: prefect-docker>=0.3.1
    image_name: europe-west2-docker.pkg.dev/<org-name>/prefect-repo/atl-data-pipeline
    tag: v1
    dockerfile: ./Dockerfile
    platform: linux/amd64
    buildargs:
      GITHUB_TOKEN: '{{ $GITHUB_TOKEN }}'

# push section allows you to manage if and how this project is uploaded to remote locations
push:
- prefect_docker.deployments.steps.push_docker_image:
    requires: prefect-docker>=0.3.1
    image_name: '{{ build_image.image_name }}'
    tag: '{{ build_image.tag }}'

# pull section allows you to provide instructions for cloning this project in remote locations
pull:
- prefect.deployments.steps.set_working_directory:
    directory: /opt/prefect/atl-data-pipeline
I am using prefect v2.20.2
m
thought for 295.7 seconds
Thanks for the details — that error means the container that’s running your flow cannot see the service-account JSON at the path you’re pointing to:
Copy code
/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
Having the file next to your local
prefect_dev.yaml
doesn’t automatically make it available inside the container. You need to either bake it into the image, mount it at runtime, or avoid files entirely by using a Prefect GCP credentials block. Here are three reliable approaches for Prefect 2.20.x: 1) Recommended: use a GcpCredentials block (no file mounts) - Install the integration and register blocks once: - pip install:
pip install prefect-gcp
- register:
prefect block register -m prefect_gcp
- Create the block (one-time), either in the UI or via code by pasting the JSON as `service_account_info`:
Copy code
from prefect_gcp import GcpCredentials
  import json

  with open("amygdadss-8cde8d989be6.json") as f:
      info = json.load(f)

  GcpCredentials(service_account_info=info).save("my-gcp-creds", overwrite=True)
- Load and use in your flow:
Copy code
from prefect import flow
  from prefect_gcp import GcpCredentials
  from google.cloud import storage

  @flow
  def run():
      gcp = GcpCredentials.load("my-gcp-creds")
      client = storage.Client(credentials=gcp.get_credentials_from_service_account())
      print([b.name for b in client.list_buckets(project="my-project")])

  if __name__ == "__main__":
      run()
- Docs: GcpCredentials block, prefect-gcp overview 2) Mount the JSON into the container and set GOOGLE_APPLICATION_CREDENTIALS - If you’re using a Docker work pool, add a volume and env var either on the work pool’s Base Job Template or per-deployment via
job_variables
. Example per-deployment:
Copy code
deployments:
  - name: atl-data-pipeline
    work_pool:
      name: my-docker-pool
    job_variables:
      image: europe-west2-docker.pkg.dev/<org-name>/prefect-repo/atl-data-pipeline:v1
      env:
        GOOGLE_APPLICATION_CREDENTIALS: /opt/prefect/atl-data-pipeline/sa.json
      volumes:
        - "/abs/path/on/worker-host/sa.json:/opt/prefect/atl-data-pipeline/sa.json:ro"
Important: - The left side of the volume mapping must exist on the machine running the Docker worker (not your laptop). - Set the env var to the container path. - Docs: Docker worker, Work pools, Overriding job variables 3) Copy the JSON into the image (works, but be careful with secrets) - In your Dockerfile:
Copy code
COPY amygdadss-8cde8d989be6.json /opt/prefect/atl-data-pipeline/sa.json
  ENV GOOGLE_APPLICATION_CREDENTIALS=/opt/prefect/atl-data-pipeline/sa.json
Then rebuild/push the image. Ensure your
.dockerignore
is not excluding the JSON if you intend to copy it. This approach embeds credentials in the image, so only use it if you’re comfortable with that risk and with your registry access controls. A couple of clarifications that might help pinpoint your current issue: - How are you setting
GOOGLE_APPLICATION_CREDENTIALS
right now? Is it in your Dockerfile, work pool base job template, or code? - Which worker type are you using (Docker or Kubernetes)? If Kubernetes, you’ll want to mount the file via a Secret/volume and set the env var in the job manifest template. - Can you share your Dockerfile and, if using a Docker work pool, the Base Job Template or
job_variables
you’re setting? Also note: the
pull: - prefect.deployments.steps.set_working_directory
just sets the working directory inside the runtime; it does not copy secrets into the image or mount files. Helpful docs:
- Docker deployment guide - Kubernetes deployment guide If you share your worker type and how you’re currently setting the env var, I can give you an exact config snippet to fix this.
d
@Marvin this is the dockerfile I am using:
Copy code
# Use the official Python image as a base image
FROM --platform=linux/amd64 python:3.10.12-slim

# Set the working directory in the container
WORKDIR /opt/prefect/atl-data-pipeline

# Install system dependencies, including Git and g++
RUN apt-get update && apt-get install -y \
    gcc \
    g++ \
    libpq-dev \
    git \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Set up build arguments to pass the GitHub token
ARG GITHUB_TOKEN

# Set environment variables
ENV GITHUB_TOKEN=$GITHUB_TOKEN
# ENV GOOGLE_APPLICATION_CREDENTIALS=/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json

# Set Git buffer size to allow AmygdaAnalytics lib to be installed
RUN git config --global http.postBuffer 104857600

COPY requirements.txt .

# Modify requirements.txt to exclude shap
RUN sed -i '/shap/d' requirements.txt

# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy directories
COPY flows flows
COPY src src

# Copy individual files
COPY .env .
COPY .env.prod .
COPY .prefectignore .
COPY Dockerfile .
COPY prefect_dev.yaml .
COPY amygdadss-8cde8d989be6.json .

ENTRYPOINT ["python"]
m
thought for 77.6 seconds
Thanks for sharing the Dockerfile — this helps. A few key points based on what you’ve posted: - Your Dockerfile does copy the JSON into the image at
/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
, so the path in the error should exist in the image if the correct image/tag is running. - Your
ENV GOOGLE_APPLICATION_CREDENTIALS=...
line is commented out. Without it (or equivalent code that loads the JSON explicitly), Google auth won’t know to use that file. However, since the error specifically mentions that exact path, something is setting
GOOGLE_APPLICATION_CREDENTIALS
to that value at runtime (perhaps your work pool/job variables, cluster env, or code). Given the above, there are two likely failure modes: 1) The running container does not have the file (tag mismatch, old image pulled by the worker, or different image referenced in the deployment/work pool). 2)
GOOGLE_APPLICATION_CREDENTIALS
is set in the container but points to a path where the file does not exist. Quick checks to confirm: - Verify the file is in the image you’re actually running:
Copy code
docker pull europe-west2-docker.pkg.dev/<org-name>/prefect-repo/atl-data-pipeline:v1
  docker run --rm -it europe-west2-docker.pkg.dev/<org-name>/prefect-repo/atl-data-pipeline:v1 ls -l /opt/prefect/atl-data-pipeline
You should see
amygdadss-8cde8d989be6.json
in the listing. - Confirm the exact image/tag your deployment is using and that your worker is pulling that tag (workers may cache images). Three reliable ways to fix it: A) Keep the file in the image and set the env var in the image - Un-comment the env line, rebuild, and push:
Copy code
ENV GOOGLE_APPLICATION_CREDENTIALS=/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
Then rebuild/push and redeploy with the same tag, or bump the tag and update the deployment. - To “fail fast” at build time if the file goes missing:
Copy code
RUN test -f /opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
B) Mount the JSON at runtime and set the env var via the work pool - If you’re using a Docker work pool, define
volumes
and
env
in the Base Job Template or per-deployment `job_variables`:
Copy code
job_variables:
    image: europe-west2-docker.pkg.dev/<org-name>/prefect-repo/atl-data-pipeline:v1
    env:
      GOOGLE_APPLICATION_CREDENTIALS: /opt/prefect/atl-data-pipeline/sa.json
    volumes:
      - "/abs/path/on/worker-host/sa.json:/opt/prefect/atl-data-pipeline/sa.json:ro"
Notes: - The left side must exist on the machine running the Docker worker (not your laptop). - The env var must point to the container path. - Docs: Docker worker, Work pools, Overriding job variables C) Recommended long-term: use a Prefect GcpCredentials block (no file in image) - Install/register once:
Copy code
pip install prefect-gcp
  prefect block register -m prefect_gcp
- Create the block with the JSON contents (one-time):
Copy code
from prefect_gcp import GcpCredentials
  import json

  with open("amygdadss-8cde8d989be6.json") as f:
      info = json.load(f)

  GcpCredentials(service_account_info=info).save("my-gcp-creds", overwrite=True)
- Load and use in your flow (no GOOGLE_APPLICATION_CREDENTIALS needed):
Copy code
from prefect import flow
  from prefect_gcp import GcpCredentials
  from google.cloud import storage

  @flow
  def run():
      gcp = GcpCredentials.load("my-gcp-creds")
      client = storage.Client(credentials=gcp.get_credentials_from_service_account())
      print([b.name for b in client.list_buckets(project="my-project")])

  if __name__ == "__main__":
      run()
- Docs: GcpCredentials block, prefect-gcp overview A few follow-ups to tailor an exact fix: - Which worker type are you using (Docker or Kubernetes)? - Where is
GOOGLE_APPLICATION_CREDENTIALS
being set right now (Dockerfile, work pool job variables, or elsewhere)? - What image reference (repo:tag) is your deployment using? If you can share your work pool type and the deployment’s image and job variables, I’ll provide a concrete snippet aligned to your setup.
d
@Marvin when i run those docker commands it cant see that directory:
Copy code
angel-class180-project % docker pull europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1
v1: Pulling from amygdadss/prefect-repo/atl-data-pipeline
Digest: sha256:a1e135bcf4d0a242e03cf55cfc80ebb079cad8ca16e3aa33f51569488c72f6fa
Status: Image is up to date for europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1
europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1
(atl_env) dansimons@Daniels-MacBook-Pro-5 angel-class180-project % docker run --rm -it europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1 ls -l /opt/prefect/atl-data-pipeline
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
python: can't open file '/opt/prefect/atl-data-pipeline/ls': [Errno 2] No such file or directory
(atl_env) dansimons@Daniels-MacBook-Pro-5 angel-class180-project %
m
thought for 52.2 seconds
Great clue — the “python: can’t open file …/ls” message explains what’s happening. Your image sets
ENTRYPOINT ["python"]
, so when you run:
Copy code
docker run ... IMAGE ls -l /opt/prefect/atl-data-pipeline
Docker treats
ls -l /opt/prefect/atl-data-pipeline
as arguments to the entrypoint, i.e. it tries to run:
Copy code
python ls -l /opt/prefect/atl-data-pipeline
…which fails. Do this to inspect the image correctly (also add
--platform linux/amd64
on Apple Silicon):
Copy code
docker run --rm -it --platform linux/amd64 --entrypoint /bin/sh europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1 -c "ls -l /opt/prefect/atl-data-pipeline; echo; echo GOOGLE_APPLICATION_CREDENTIALS=\$(printenv GOOGLE_APPLICATION_CREDENTIALS)"
A few concrete fixes and checks: 1) Verify the JSON is actually in the image/tag your worker is using - If you don’t see the JSON after the command above, the pushed image likely doesn’t include it (e.g., built before you added
COPY amygdadss-...json .
). - Rebuild and push with the correct platform and a new tag to avoid cache confusion:
Copy code
docker buildx build --platform linux/amd64 -t europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2 .
  docker push europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
- Then re-check with:
Copy code
docker run --rm -it --platform linux/amd64 --entrypoint /bin/sh europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2 -c "ls -l /opt/prefect/atl-data-pipeline"
2) Set GOOGLE_APPLICATION_CREDENTIALS in a reliable place - Since you’re copying the JSON into the image, un-comment this in your Dockerfile so it’s set everywhere the image runs:
Copy code
ENV GOOGLE_APPLICATION_CREDENTIALS=/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
Rebuild/push and update your deployment to use the new tag (or set pull policy to always pull). - Alternatively, set the env var in your Docker work pool’s Base Job Template or per-deployment
job_variables.env
. Example per-deployment:
Copy code
job_variables:
    image: europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
    pull_policy: ALWAYS
    env:
      GOOGLE_APPLICATION_CREDENTIALS: /opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
Docs: Docker worker, Work pools, Overriding job variables 3) Strongly consider removing the ENTRYPOINT - With Prefect Docker workers, the worker sets the container command to run your flow. Having
ENTRYPOINT ["python"]
can interfere and make debugging harder (as you just experienced). It’s safer to remove that line so the worker’s command runs as intended. 4) Optional but recommended: use a GcpCredentials block instead of embedding the file - This avoids shipping secrets in the image or mounting files: - Install/register once:
Copy code
pip install prefect-gcp
    prefect block register -m prefect_gcp
- Create the block (one-time) with your JSON contents:
Copy code
from prefect_gcp import GcpCredentials
    import json
    GcpCredentials(service_account_info=json.load(open("amygdadss-8cde8d989be6.json"))).save("my-gcp-creds", overwrite=True)
- Load in your flow:
Copy code
from prefect_gcp import GcpCredentials
    gcp = GcpCredentials.load("my-gcp-creds")
    creds = gcp.get_credentials_from_service_account()
Docs: GcpCredentials block If you share: - the worker type (Docker or Kubernetes), - the deployment’s image tag, - and whether you prefer env var or Prefect block, I can give you an exact snippet to match your setup.
d
@Marvin the file is clearly in the container:
Copy code
(atl_env) dansimons@Daniels-MacBook-Pro-5 angel-class180-project % docker run --rm -it --platform linux/amd64 --entrypoint /bin/sh europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1 -c "ls -l /opt/prefect/atl-data-pipeline; echo; echo GOOGLE_APPLICATION_CREDENTIALS=\$(printenv GOOGLE_APPLICATION_CREDENTIALS)"
total 20
-rw-r--r-- 1 root root 1041 Sep 17  2024 Dockerfile
-rw-r--r-- 1 root root 2337 Sep 10  2024 amygdadss-8cde8d989be6.json
drwxr-xr-x 1 root root   44 Aug 19 12:06 flows
-rw-r--r-- 1 root root 7767 Aug 19 09:46 prefect_dev.yaml
-rw-r--r-- 1 root root  355 Jul  1 14:20 requirements.txt
drwxr-xr-x 1 root root  350 Aug 19 12:06 src

GOOGLE_APPLICATION_CREDENTIALS=
m
thought for 107.2 seconds
Awesome — that output shows: - The service-account file is in the image at /opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json - GOOGLE_APPLICATION_CREDENTIALS is not set in the image So the DefaultCredentialsError you’re seeing happens because something in your runtime is telling Google auth to look at that path, but the env var isn’t actually set in the image you’re running locally. This usually means: - The env var is being set elsewhere (e.g., in your work pool’s Base Job Template or deployment job_variables), or - The worker is pulling a different image/tag than the one you inspected Two straightforward ways to fix it Option A — Set the env var in the image - Un-comment the env line in your Dockerfile:
Copy code
ENV GOOGLE_APPLICATION_CREDENTIALS=/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
- Rebuild and push with a new tag (on Apple Silicon, build for amd64):
Copy code
docker buildx build --platform linux/amd64 -t europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2 .
docker push europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
- Update your deployment to use
:v2
and ensure your Docker work pool uses a pull policy that fetches the new tag:
Copy code
job_variables:
  image: europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
  pull_policy: ALWAYS
Option B — Set the env var via the Docker work pool (no Dockerfile change) - Keep the file in the image and set the env var per-deployment (or in the Base Job Template):
Copy code
job_variables:
  image: europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1
  pull_policy: ALWAYS
  env:
    GOOGLE_APPLICATION_CREDENTIALS: /opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
- This tells the container to use the file you’ve already baked into the image. Important checks - Make sure the deployment’s image tag matches the image that actually contains the JSON (consider bumping to v2 to avoid cache confusion). - Ensure the Docker worker is pulling the new image (pull_policy: ALWAYS), or restart the worker to clear any cached image. - Consider removing the ENTRYPOINT ["python"] from the Dockerfile — Docker workers set the container command, and a custom entrypoint can interfere and makes debugging like this harder. Alternative: Use a GcpCredentials block (recommended long-term) - Avoids shipping the JSON in your image:
Copy code
pip install prefect-gcp
prefect block register -m prefect_gcp
Create the block once:
Copy code
from prefect_gcp import GcpCredentials
import json
GcpCredentials(service_account_info=json.load(open("amygdadss-8cde8d989be6.json"))).save("my-gcp-creds", overwrite=True)
Load in your flow:
Copy code
from prefect_gcp import GcpCredentials
from google.cloud import storage

gcp = GcpCredentials.load("my-gcp-creds")
client = storage.Client(credentials=gcp.get_credentials_from_service_account())
Docs for reference: - Docker worker: https://docs-2.prefect.io/latest/integrations/prefect-docker/worker/ - Work pools and job variables: https://docs-2.prefect.io/latest/concepts/work-pools/ and https://docs-2.prefect.io/latest/guides/deployment/overriding-job-variables/ - GCP credentials block: https://docs-2.prefect.io/latest/integrations/prefect-gcp/credentials/ If you can share: - Your worker type (Docker or Kubernetes) - How you’re currently setting env vars (Dockerfile vs work pool job_variables) - The exact image tag referenced by the deployment …I can give you a precise snippet tailored to your setup.
d
@Marvin is this the equivelant:
Copy code
docker buildx build --platform linux/amd64 -t europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2 .
docker push europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
of this: prefect deploy --name atl-full-day-flow_dev --prefect-file prefect_dev.yaml
m
thought for 64.6 seconds
Short answer: no — they’re not equivalent. - docker buildx build + docker push only builds and pushes your image. - prefect deploy reads your prefect_dev.yaml, runs the build/push steps you’ve defined, and also creates/updates the Prefect deployment (including the image reference, env/job variables, schedules, etc.). When to use which: - If you only changed the image contents (e.g., un-commented the GOOGLE_APPLICATION_CREDENTIALS ENV) and will keep the same image tag that your deployment already points to, you can just rebuild/push and make sure the worker pulls the new image:
Copy code
docker buildx build --platform linux/amd64 -t europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1 .
  docker push europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v1
Ensure your Docker work pool pulls fresh images (pull_policy: ALWAYS) or restart the worker. - If you’re changing the image tag or any deployment metadata, run prefect deploy (or update the deployment in the UI). Example:
Copy code
docker buildx build --platform linux/amd64 -t europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2 .
  docker push europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
Then update the deployment to use :v2 — either by: - Editing your prefect_dev.yaml and running:
Copy code
prefect deploy --name atl-full-day-flow_dev --prefect-file prefect_dev.yaml
- Or editing the deployment’s job variables in the UI to set:
Copy code
image: europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
    pull_policy: ALWAYS
Tip: Since your image already contains the JSON file, set the env var either in the Dockerfile:
Copy code
ENV GOOGLE_APPLICATION_CREDENTIALS=/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
or in the deployment/job variables:
Copy code
env:
  GOOGLE_APPLICATION_CREDENTIALS: /opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
Related docs: - Docker worker: https://docs-2.prefect.io/latest/integrations/prefect-docker/worker/ - Work pools and job variables: https://docs-2.prefect.io/latest/concepts/work-pools/ - Overriding job variables in deployments: https://docs-2.prefect.io/latest/guides/deployment/overriding-job-variables/
d
@Marvin I built and redeployed the image using those two docker commands and I still get the same json not found error
m
thought for 267.8 seconds
Got it — since you rebuilt/pushed and still see “File … was not found”, there are two common culprits: 1) Your deployment is not actually running the image you think (old tag or different image in the deployment/work pool) 2) The worker is not pulling the updated image (pull policy) and is running a cached image that doesn’t contain the file Let’s verify and fix: 1) Inspect the deployment to confirm the image and env used at runtime - Run:
Copy code
prefect deployment inspect "YOUR_FLOW_NAME/atl-full-day-flow_dev"
- Look for: - job_configuration.image - job_configuration.env (does it set GOOGLE_APPLICATION_CREDENTIALS?) - job_configuration.image_pull_policy Docs: Overriding job variables 2) Inspect the work pool to see if the Base Job Template overrides image/env - Run:
Copy code
prefect work-pool inspect <your-docker-work-pool-name>
- Check the base job template for image, env, volumes that could differ from what you expect. Docs: Work pools 3) Ensure the worker pulls your updated image - In Prefect 2.x Docker workers, the job variable is
image_pull_policy
(not
pull_policy
). Valid values: Always, IfNotPresent, Never. - Set it to Always to force fresh pulls:
Copy code
# in your prefect_dev.yaml under this deployment
deployments:
- name: atl-full-day-flow_dev
  work_pool:
    name: <your-docker-pool>
  job_variables:
    image: europe-west2-docker.pkg.dev/amygdadss/prefect-repo/atl-data-pipeline:v2
    image_pull_policy: Always
    env:
      GOOGLE_APPLICATION_CREDENTIALS: /opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
Then re-run:
Copy code
prefect deploy --name atl-full-day-flow_dev --prefect-file prefect_dev.yaml
Docs: Docker worker 4) Double-check the image actually has the file You already validated locally with:
Copy code
docker run --rm -it --platform linux/amd64 --entrypoint /bin/sh <image:tag> -c "ls -l /opt/prefect/atl-data-pipeline"
Do that on the exact tag you set in the deployment (v2, not v1), and make sure the file is listed. If you reused the same tag, the worker may still be using a cached image — that’s why step 3 is important. 5) Optional: set the env var in the image to simplify If you prefer, set it in the Dockerfile:
Copy code
ENV GOOGLE_APPLICATION_CREDENTIALS=/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json
Rebuild/push (amd64) and update the deployment to use the new tag. 6) Optional: remove ENTRYPOINT I recommend removing
ENTRYPOINT ["python"]
in your Dockerfile; Docker workers set the command for flow runs and a custom entrypoint can interfere and complicate debugging. If you want a quick runtime check, add a tiny test to your flow:
Copy code
from pathlib import Path
import os
from prefect import flow

@flow
def debug_env():
    p = Path("/opt/prefect/atl-data-pipeline/amygdadss-8cde8d989be6.json")
    print("File exists in container? ", p.exists())
    print("GOOGLE_APPLICATION_CREDENTIALS=", os.getenv("GOOGLE_APPLICATION_CREDENTIALS"))

debug_env()
Run that via the deployment to confirm what the worker container actually sees. If you share: - Your Docker work pool name - The output of
prefect deployment inspect "FLOW/atl-full-day-flow_dev"
(the image, env, image_pull_policy fields) …I’ll pinpoint the exact mismatch and provide the precise change. Long-term, consider using a Prefect GcpCredentials block to avoid shipping JSON files: GcpCredentials block.
d
@Marvin let’s try the gcp block auth way. I have ran:
Copy code
refect block register -m prefect_gcp
Successfully registered 6 blocks

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Registered Blocks             ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ BigQuery Warehouse            │
│ GCP Cloud Run Job             │
│ GCP Credentials               │
│ GcpSecret                     │
│ GCS Bucket                    │
│ Vertex AI Custom Training Job │
└───────────────────────────────┘
Now where does the actual auth code of the gcp block live in my repo? from prefect_gcp import GcpCredentials import json with open(“amygdadss-8cde8d989be6.json”) as f: info = json.load(f) GcpCredentials(service_account_info=info).save(“my-gcp-creds”, overwrite=True)
m
thought for 62.2 seconds
Great — you’ve registered the blocks. To your question: the GCP credentials “block” does not live in your repo. When you create/save it, Prefect stores it in your Prefect workspace (Cloud/Server) as a block document. Your code just loads it by name at runtime. How to proceed cleanly - One-time creation (run locally or anywhere that can reach your Prefect workspace): - Do NOT commit your JSON to the repo/image. - Run a small script that reads the local JSON and saves the block. Example:
Copy code
# create_gcp_block.py
  from prefect_gcp import GcpCredentials
  import json

  with open("amygdadss-8cde8d989be6.json") as f:
      info = json.load(f)

  GcpCredentials(service_account_info=info).save("my-gcp-creds", overwrite=True)
  print("Saved GCP block: my-gcp-creds")
- Then run:
Copy code
python create_gcp_block.py
- After this, you can delete the JSON file locally if you want. It does not need to be in your image or deployment. - Use the block in your flow (no GOOGLE_APPLICATION_CREDENTIALS needed):
Copy code
from prefect import flow
  from prefect_gcp import GcpCredentials
  from google.cloud import storage

  @flow
  def run():
      gcp = GcpCredentials.load("my-gcp-creds")
      creds = gcp.get_credentials_from_service_account()

      client = storage.Client(credentials=creds)
      for b in client.list_buckets(project="YOUR_GCP_PROJECT"):
          print(b.name)

  if __name__ == "__main__":
      run()
- Make sure your runtime image has the right dependencies: - Add to requirements.txt (or pip install in Dockerfile): - prefect-gcp - google-cloud-storage (and any other google-cloud-* libs you use) - Rebuild/push your image as needed. - Ensure your worker/flow can reach your Prefect workspace to load the block: - For Prefect Cloud, your Docker worker usually sets the required env automatically. - If needed, set
PREFECT_API_URL
and
PREFECT_API_KEY
in your worker environment. - You can also create the block via the UI (no code needed): - In Prefect UI: Blocks > + Create block > GCP Credentials > paste the JSON key as “Service Account Info” and save as “my-gcp-creds”. - Docs: GcpCredentials block What to remove now - Remove any
GOOGLE_APPLICATION_CREDENTIALS
references from your Dockerfile/deployment. You don’t need that env var when using the block. - You no longer need the JSON file inside the image. If you want, share your requirements.txt and I’ll double-check the minimal packages you’ll need for your specific GCP services.
d
@Marvin how do I use this if im using gcp in a function that is not directly in my flow code but further down e.g. here:
Copy code
import pandas as pd
import numpy as np
import warnings

import os, sys
import matplotlib.pyplot as plt
from google.cloud import storage
import re

from sqlalchemy import text

from src.common.log import logger
from src.config import Config
from pandas.api.types import is_integer_dtype

warnings.filterwarnings("ignore")
from IPython.core.ultratb import ColorTB

sys.excepthook = ColorTB()

client = storage.Client()

def rawdata_load_and_transform(file_name, bucket_name):
    <http://logger.info|logger.info>(f"Processing rawdata file: {file_name} from bucket: {bucket_name}")

    # Load Excel file from GCS
    bucket = client.get_bucket(bucket_name)
    blob = bucket.blob(file_name)
    content = blob.download_as_bytes()
    all_sheets = pd.read_excel(content, sheet_name=None)
    <http://logger.info|logger.info>(f"File {file_name} loaded from bucket {bucket_name}")
m
thought for 38.6 seconds
Great question. The cleanest way to use a GcpCredentials block when your GCS code lives “downstream” is to avoid creating a
storage.Client()
at import time and instead inject the client (or credentials) from your flow. Here are two solid patterns: Pattern A (recommended): dependency injection from the flow - Create the client in the flow using the Prefect block, then pass it into your helper functions. Flow:
Copy code
from prefect import flow
from prefect_gcp import GcpCredentials
from google.cloud import storage
from src.raw_module import rawdata_load_and_transform  # adjust import

@flow
def main(file_name: str, bucket_name: str):
    gcp = GcpCredentials.load("my-gcp-creds")
    creds = gcp.get_credentials_from_service_account()

    # Optional: pass project explicitly if needed
    storage_client = storage.Client(credentials=creds, project="YOUR_GCP_PROJECT")

    rawdata_load_and_transform(file_name, bucket_name, storage_client=storage_client)

if __name__ == "__main__":
    main("path/in/bucket.xlsx", "your-bucket")
Your module (remove the global client and accept it as a parameter):
Copy code
import pandas as pd
from google.cloud import storage
from io import BytesIO
from src.common.log import logger

def rawdata_load_and_transform(file_name, bucket_name, storage_client: storage.Client):
    <http://logger.info|logger.info>(f"Processing rawdata file: {file_name} from bucket: {bucket_name}")

    bucket = storage_client.bucket(bucket_name)  # or storage_client.get_bucket(...)
    blob = bucket.blob(file_name)
    content = blob.download_as_bytes()

    all_sheets = pd.read_excel(BytesIO(content), sheet_name=None)
    <http://logger.info|logger.info>(f"File {file_name} loaded from bucket {bucket_name}")
    return all_sheets
Benefits: - Your non-Prefect code stays decoupled from Prefect. - No reliance on GOOGLE_APPLICATION_CREDENTIALS or files in the image. - Easy to test locally by passing a default client. Pattern B: lazy creation inside the helper (introduces a Prefect dependency) - If you can tolerate a small Prefect dependency in that module, you can load the block at call time. Avoid doing it at import time.
Copy code
from functools import lru_cache
from google.cloud import storage

@lru_cache(maxsize=1)
def _get_storage_client():
    try:
        from prefect_gcp import GcpCredentials
        gcp = GcpCredentials.load("my-gcp-creds")
        creds = gcp.get_credentials_from_service_account()
        return storage.Client(credentials=creds, project="YOUR_GCP_PROJECT")
    except Exception:
        # Fallback to ADC (e.g., local dev with gcloud auth application-default login)
        return storage.Client()

def rawdata_load_and_transform(file_name, bucket_name):
    client = _get_storage_client()
    bucket = client.bucket(bucket_name)
    blob = bucket.blob(file_name)
    content = blob.download_as_bytes()
    ...
Important: remove any top-level
client = storage.Client()
in your modules. Initializing the client at import time will bypass the block and rely on ADC, which is what caused the “JSON not found” error previously. Don’t forget runtime deps - Ensure your image includes: -
prefect-gcp
-
google-cloud-storage
(and any other google-cloud-* libs you use) - Rebuild/push your image after updating requirements. Docs for reference: - GcpCredentials block If you share your flow entrypoint and where this helper lives, I can suggest the exact edits to wire it up end-to-end.