<@ULVA73B9P> how to trigger a deployment using .de...
# ask-marvin
v
@Marvin how to trigger a deployment using .deploy() command to a remote prefect server url. Even after setting os.environ["PREFECT_API_URL"] to the remote one, inside .deploy() it is pointing to localhost api url again for getting workpool.
m
thought for 415.6 seconds
It sounds like
Flow.deploy()
is reading a local API URL from your active settings context, so your
os.environ["PREFECT_API_URL"]
change isn’t taking effect when the client is created to look up the work pool. In Prefect 3.x, the API URL used by
Flow.deploy()
comes from the current Prefect settings in this precedence order: 1) temporary_settings() context 2) Environment variables (PREFECT_API_URL, PREFECT_API_KEY) 3) .env / prefect.toml / pyproject.toml in CWD 4) Active profile (~/.prefect/profiles.toml) 5) Defaults Reliable ways to force a remote API URL Option A — Explicit in code (recommended) Wrap your deploy in a temporary settings override. This takes highest precedence and guarantees the value used by
get_client()
inside
.deploy()
.
Copy code
from prefect import flow
from prefect.settings import PREFECT_API_URL, temporary_settings

@flow
def my_flow():
    pass

if __name__ == "__main__":
    with temporary_settings({PREFECT_API_URL: "<https://your-remote-host/api>"}):
        print("API:", PREFECT_API_URL.value())  # sanity check
        my_flow.deploy(
            name="my-deployment",
            work_pool_name="your-remote-pool",
        )
Option B — Set env vars before starting Python Set variables in your shell (or CI) so Prefect reads them when creating settings for the process.
Copy code
export PREFECT_API_URL=<https://your-remote-host/api>
export PREFECT_API_KEY=YOUR_KEY_IF_NEEDED
python deploy_script.py
Option C — Use a Prefect profile Create/switch to a profile that points at your remote server, then run your script.
Copy code
prefect profile create remote
prefect profile use remote
prefect config set PREFECT_API_URL=<https://your-remote-host/api>
prefect config set PREFECT_API_KEY=YOUR_KEY_IF_NEEDED
python deploy_script.py
Quick sanity check Before calling
.deploy()
, print the resolved value:
Copy code
from prefect.settings import PREFECT_API_URL
print("Resolved API URL:", PREFECT_API_URL.value())
If it’s still
<http://localhost:4200/api>
, something else in your settings stack is overriding your env var (e.g., an active profile, .env, or a prior settings context). Using
temporary_settings(...)
will override all of those. A few extra tips - Make sure the work pool exists on the remote server (and not only locally). - If your remote is Prefect Cloud or a self-hosted server with auth, also set
PREFECT_API_KEY
. - If you prefer profiles in code, you can also do:
Copy code
from prefect.context import use_profile
with use_profile("remote"):
    my_flow.deploy(name="...", work_pool_name="...")
Docs links - Settings & profiles (precedence, env, profiles): Settings and profiles - Manage settings: How to manage settings - Deploy via Python: Deploy via Python If this still points to localhost, can you share: - Where in your script you set
os.environ["PREFECT_API_URL"]
- A minimal snippet around your
flow.deploy(...)
- Output of
print(PREFECT_API_URL.value())
right before the deploy I’ll help pinpoint what’s overriding it.
v
@Marvin client object (prefect.client.orchestration.PrefectClient Object) has still the api url as localhost
m
thought for 83.1 seconds
Got it — if your PrefectClient still shows localhost, it usually means the client was created before your settings change took effect, or a settings context/profile is overriding your env var. Key points: - PrefectClient captures the API URL at instantiation. If you change env vars afterward, the existing client won’t update — you must create a new client after the settings are applied. - Settings precedence: temporary_settings() > env vars > .env/prefect.toml/pyproject.toml > active profile > defaults. Do this to force a remote API for deploy and for any client you create:
Copy code
from prefect import flow
from prefect.client.orchestration import get_client
from prefect.settings import PREFECT_API_URL, PREFECT_API_KEY, temporary_settings

@flow
def my_flow():
    pass

if __name__ == "__main__":
    with temporary_settings({
        PREFECT_API_URL: "<https://your-remote-host/api>",
        # include if your server requires auth
        # PREFECT_API_KEY: "pnu_..."
    }):
        # Sanity check
        print("Resolved API URL:", PREFECT_API_URL.value())

        # Any client created here will use the remote URL
        async def check():
            async with get_client() as client:
                print("Client API URL:", client.api)

        import anyio
        anyio.run(check)

        # Deploy will also use the same settings
        my_flow.deploy(
            name="my-deployment",
            work_pool_name="your-remote-pool",
        )
If you prefer env vars, set them before starting Python (not inside the script), then start a fresh process so a new client is created with the right settings:
Copy code
export PREFECT_API_URL=<https://your-remote-host/api>
export PREFECT_API_KEY=YOUR_KEY_IF_NEEDED
python deploy_script.py
Or use a profile and switch before running your script:
Copy code
prefect profile create remote
prefect profile use remote
prefect config set PREFECT_API_URL=<https://your-remote-host/api>
prefect config set PREFECT_API_KEY=YOUR_KEY_IF_NEEDED
python deploy_script.py
Debugging tips: - See what is setting the URL:
Copy code
prefect config view --show-sources
- Print the resolved setting right before you create the client or call `.deploy()`:
Copy code
from prefect.settings import PREFECT_API_URL
print("Resolved API URL:", PREFECT_API_URL.value())
- If you created a PrefectClient before changing settings, discard it and create a new one after the change (or let
.deploy()
create its own via
get_client()
inside a
temporary_settings(...)
block). Docs: - Settings and profiles (precedence, env, profiles): Settings and profiles - Manage settings: How to manage settings - Deploy via Python: Deploy via Python If you’re still seeing localhost, can you share: - A minimal snippet showing where you set
PREFECT_API_URL
and where you create/use the client - Output of
print(PREFECT_API_URL.value())
right before your
.deploy()
- Output of
prefect config view --show-sources | grep -A2 api.url
I’ll help pinpoint what’s overriding it.
v
@Marvin I am using flow.from_source and pointing to the github location where the flow function is written. In the code where I am scheduling the deployment I am using GithubCredentials with token, so it is fine. But at the scheduled time when the worker tries to clone the repo, it does not have access to token. The worker is started using docker compose file in another server
m
thought for 155.2 seconds
Thanks — that context helps. What’s happening is that the GitHubCredentials you used when creating the deployment are not automatically available to the worker at run time unless you either: - attach credentials to the repository in
from_source(...)
so the pull step embeds them, or - give the worker an auth mechanism it can use on its own (SSH key, env, GIT_ASKPASS), or - define credentials via pull steps in prefect.yaml. Here are the reliable patterns for private Git repos with Flow.from_source in Prefect 3.x. Option 1 — Pass a credentials block to GitRepository (recommended) This serializes credentials into the deployment’s pull step so the worker can clone with a token at runtime.
Copy code
from prefect import flow
from prefect.runner.storage import GitRepository
from prefect_github import GitHubCredentials  # pip install prefect-github

# Save once (UI or code)
# GitHubCredentials(token="ghp_...").save("my-github-creds", overwrite=True)

repo = GitRepository(
    url="<https://github.com/org/private-repo.git>",
    branch="main",
    credentials=GitHubCredentials.load("my-github-creds"),
)

flow.from_source(
    source=repo,
    entrypoint="path/to/flow_file.py:my_flow",
).deploy(
    name="my-deployment",
    work_pool_name="my-docker-pool",
)
- At run time, the worker runs the pull step, which formats the URL like
https://<token>@github.com/org/private-repo.git
and clones successfully. - Use the provider-specific collection that implements credentials formatting: - GitHub:
prefect-github
- GitLab:
prefect-gitlab
- Bitbucket:
prefect-bitbucket
Docs: - Store flow code (Git auth, pull steps): Store flow code - GitHub integration: prefect-github Option 2 — Define pull steps in prefect.yaml If you prefer yaml:
Copy code
pull:
  - prefect.deployments.steps.git_clone:
      repository: <https://github.com/org/private-repo.git>
      credentials: "{{ prefect.blocks.github-credentials.my-github-creds }}"
      branch: main

deployments:
  - name: my-deployment
    entrypoint: path/to/flow_file.py:my_flow
    work_pool:
      name: my-docker-pool
Option 3 — SSH URL and mount SSH keys into worker Use an SSH clone URL and give the worker a key. - Use SSH URL in your repo definition:
Copy code
repo = GitRepository(
      url="git@github.com:org/private-repo.git",
      branch="main",
  )
- In your docker compose for the worker, mount keys and known_hosts:
Copy code
services:
    prefect-worker:
      image: prefecthq/prefect:3-latest
      environment:
        PREFECT_API_URL: <http://your-prefect-server:4200/api>
      volumes:
        - ~/.ssh:/root/.ssh:ro
        - /etc/ssh/ssh_config:/etc/ssh/ssh_config:ro   # optional
      command: prefect worker start --pool my-docker-pool
- Ensure the public key is added to the GitHub account and known_hosts includes github.com (first connection will add it, or seed known_hosts). Option 4 — Env-based token with GIT_ASKPASS (less recommended) If you can’t use blocks, you can configure GIT_ASKPASS or embed token via environment: - Provide a script inside the worker image that echoes the token, then set:
Copy code
environment:
    GIT_ASKPASS: /opt/git-askpass.sh
    GITHUB_TOKEN: ${GITHUB_TOKEN}
And your `/opt/git-askpass.sh`:
Copy code
#!/bin/sh
  echo "$GITHUB_TOKEN"
- Or embed the token in the URL in the pull step (be careful with logs and security). Why your current setup fails - Using GitHubCredentials only in the “scheduling/deploy” script doesn’t help at run time unless those credentials are attached to the
GitRepository
passed to
from_source
(or defined in yaml pull steps). The worker runs in a different environment and needs its own way to authenticate at execution time. - Workers started via docker compose on another server have no access to your local env unless you mount or pass it explicitly.
Checklist to fix quickly 1) Switch your
from_source
to use a
GitRepository
with a credentials block as shown above, and redeploy. 2) Ensure your worker has network access to github.com and correct time/CA certs. 3) If using SSH, mount
~/.ssh
into the worker container and use the SSH URL. 4) Avoid putting tokens directly in the repo URL in code; prefer credentials blocks or SSH. If you share: - Your current
from_source(...)
snippet - Your docker-compose service for the worker - Whether you prefer PAT or SSH …I can tailor an exact working example for your setup.
v
@Marvin I am using GithubHub credentials block to save and use the credentials. And I am using .deploy() fucntion to schedule the depoyment instead of a yaml. So the worker should automatically get the credentials there? But the worker is throwing error not able to clone. And also is there any way to debug the code that worker runs (flow_runs) to go line by line and see the issue
m
thought for 61.8 seconds
Short answer: yes, when you pass a GitHubCredentials block to the GitRepository in
Flow.from_source(...)
, Prefect includes those credentials in the deployment’s pull step so the worker can authenticate at runtime. If your worker can’t clone, it’s usually a runtime environment issue rather than the block not being attached. Common causes and fixes 1) Work pool type and where the pull happens - Docker work pool: the pull step runs inside the job container image, not in the worker container. That image must contain git (and CA certs) and have the integration installed if needed. - Process/local work pool: the pull step runs on the worker host/container, so that host/container must have git and proper certs. 2) Ensure you’re attaching credentials to the GitRepository used by from_source - Good pattern:
Copy code
from prefect import flow
from prefect.runner.storage import GitRepository
from prefect_github import GitHubCredentials  # pip install prefect-github

repo = GitRepository(
    url="<https://github.com/org/private-repo.git>",
    branch="main",
    credentials=GitHubCredentials.load("my-github-creds"),
)

flow.from_source(
    source=repo,
    entrypoint="path/to/flow.py:my_flow",
).deploy(
    name="my-deployment",
    work_pool_name="my-docker-pool",
    # if using Docker work pool, specify an image that has git installed:
    image="my-registry/prefect-git:latest",
)
3) Your runtime image must include git (and likely the integration package) - If you’re using a Docker work pool, build an image with git and ca-certificates installed:
Copy code
# Dockerfile
FROM prefecthq/prefect:3-latest
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir "prefect-github>=0.3.0"
- Push this image and use it in
.deploy(image=...)
. 4) Use HTTPS URL with GitHubCredentials - The GitHubCredentials formatter expects an https repo URL. If you want SSH, don’t use the credentials block—use an SSH URL and mount SSH keys into the runtime environment instead. 5) Token scopes and submodules - Ensure the token has
repo
scope and access to any private submodules. - If your repo uses private submodules, consider enabling submodule support in your GitRepository and ensure the token can access them. 6) Verify the deployment captured the credentials - In the UI, open the deployment and look at the Pull section. You should see a git_clone step with credentials (token will be redacted). - If it’s missing, revisit your
from_source(..., source=GitRepository(credentials=...))
code and redeploy. 7) Network and SSL - Confirm outbound egress to github.com from the job container. - Install CA certificates (as above) to avoid TLS errors. How to debug what the worker/job is doing - Increase logging: - Set
PREFECT_LOGGING_LEVEL=DEBUG
on the worker (and for docker jobs, in the job environment/image). - Reproduce inside the job image: - Run an interactive shell in the same image the deployment uses:
Copy code
docker run -it --rm my-registry/prefect-git:latest bash
    git --version
    # Try a tokened URL (replace <TOKEN> and repo)
    git ls-remote https://<TOKEN>@github.com/org/private-repo.git
- This confirms git and certs are present and the token works. - Check the Pull section logs in the flow run details (UI) to see the error (403 vs DNS vs TLS). - If you need step-by-step debugging of flow code, run locally: - Switch to a process/local pool and run the worker on your dev machine so you can attach a debugger. - Or run the same code locally (with the same
from_source(...)
) to reproduce before deploying. - You can also add a temporary shell pull step (if you move to prefect.yaml) that prints
git --version
and
git config -l
to verify the environment. With pure
.deploy()
you can’t add custom pull steps in Python, so environment verification is best done by testing the job image interactively as above. Next steps / quick checklist
- What work pool type are you using (docker or process)? - What image does the deployment use? - Does that image have git and ca-certificates installed? - Can you share the exact error message from the worker logs? - Confirm your repo URL is https and that you’re passing
credentials=GitHubCredentials.load("...")
into
GitRepository
. Docs - Store flow code (Git auth, pull steps): Store flow code - Docker work pools: Docker work pools - GitHub integration: prefect-github Share the error text and your deploy snippet + work pool type, and I’ll give you an exact fix for your setup.