Henrik Löf
10/17/2025, 9:26 AMMarvin
10/17/2025, 9:26 AMMarvin
10/17/2025, 9:29 AMname: Deploy Prefect
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
env:
PREFECT_API_KEY: ${{ secrets.PREFECT_API_KEY }} # Service Account key
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Prefect
run: pip install -U prefect
- name: Login to Prefect Cloud
run: prefect cloud login --key "$PREFECT_API_KEY" --workspace "<account_handle>/<workspace_handle>"
- name: Deploy
run: prefect deploy
Option B: Use environment variables (no CLI login)
- Set both PREFECT_API_KEY and PREFECT_API_URL.
- PREFECT_API_URL must be the workspace-scoped API:
https://api.prefect.cloud/api/accounts/<ACCOUNT-ID>/workspaces/<WORKSPACE-ID>
- You can get the IDs from your app URL:
https://app.prefect.cloud/account/<ACCOUNT-ID>/workspace/<WORKSPACE-ID>/…
- Example (GitHub Actions):
name: Deploy Prefect
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
env:
PREFECT_API_KEY: ${{ secrets.PREFECT_API_KEY }}
PREFECT_API_URL: ${{ secrets.PREFECT_API_URL }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Prefect
run: pip install -U prefect
- name: Deploy
run: prefect deploy
Notes and common pitfalls
- Service Accounts are always created at the Account level in Prefect 3.x; that’s expected. You must explicitly share them with each workspace and assign the correct workspace role.
- 401 Unauthorized typically means the key is invalid or mistyped. 403 Forbidden typically means the Service Account is not shared with the workspace or lacks sufficient role permissions.
- Make sure you run prefect deploy from a project containing a valid prefect.yaml, or provide an explicit entrypoint if you’re not using a project.
- Starter plan supports Service Accounts for CI/CD; there’s no plan restriction blocking this workflow.
Docs for reference
- Service Accounts: Service accounts
- Connect to Cloud (API key, profiles, URL): Connect to Cloud
- CI/CD with GitHub Actions: CI/CD deployments
If it’s still failing, can you share:
- The exact error message you’re seeing
- Which auth pattern you’re using (cloud login vs env vars)
- Confirmation that the Service Account was added to the target workspace and its role
- The workspace handle or the account/workspace IDs you’re targeting
I’m happy to help debug your YAML or commands once I see those details.Henrik Löf
10/17/2025, 9:33 AMHenrik Löf
10/17/2025, 9:35 AMMarvin
10/17/2025, 9:35 AMMarvin
10/17/2025, 9:37 AMprefect cloud login --key "<YOUR_ADMIN_OR_SERVICE_ACCOUNT_KEY>" --workspace "<account_handle>/<workspace_handle>"
prefect cloud ip-allowlist ls # sanity check current entries
prefect cloud ip-allowlist disable # turn off enforcement
2) Keep the allowlist and use a static egress IP
- Run your CI/CD from a self-hosted runner or any environment with a stable, known egress IP (or NAT gateway).
- Then add that IP (or a CIDR) to the allowlist:
prefect cloud login --key "<YOUR_ADMIN_OR_SERVICE_ACCOUNT_KEY>" --workspace "<account_handle>/<workspace_handle>"
prefect cloud ip-allowlist add <IP-or-CIDR> --description "CI runner"
prefect cloud ip-allowlist enable
Notes and gotchas
- You must perform allowlist changes from a currently allowed IP (to prevent lockouts).
- GitHub-hosted runners are not suitable for strict IP allowlisting due to dynamic IPs.
- After resolving the network restriction, make sure the Service Account is shared with the target workspace and has a role that can manage deployments.
- For CI auth, either:
- Use prefect cloud login --key ... --workspace <account_handle>/<workspace_handle>, or
- Set PREFECT_API_KEY and PREFECT_API_URL to the workspace URL <https://api.prefect.cloud/api/accounts/<ACCOUNT-ID>/workspaces/<WORKSPACE-ID>>.
Docs
- Secure access by IP address: IP allowlist
- Connect to Cloud: Connect to Cloud
- CI/CD deployments: CI/CD deployments
Quick questions so I can tailor the next steps:
- Are you using GitHub-hosted runners or self-hosted?
- Do you want to keep IP allowlisting enabled, or is disabling it acceptable for your workflow?Henrik Löf
10/17/2025, 9:39 AMMarvin
10/17/2025, 9:39 AMMarvin
10/17/2025, 9:40 AMprefect cloud login --key "<API_KEY>" --workspace "<account_handle>/<workspace_handle>"
prefect cloud ip-allowlist ls
- If you see entries and it’s enabled, that explains the block.
- You can temporarily turn it off:
prefect cloud ip-allowlist disable
Note: these commands work only if the feature is enabled on your account and you’re connecting from an allowed IP.
2) Double-check your targeting:
- If you’re using env vars, ensure PREFECT_API_URL is the workspace-scoped URL:
https://api.prefect.cloud/api/accounts/<ACCOUNT-ID>/workspaces/<WORKSPACE-ID>
- Or, use:
prefect cloud login --key "<API_KEY>" --workspace "<account_handle>/<workspace_handle>"
A wrong account/workspace will also trigger that error.
If you do need an IP allowlist
- GitHub-hosted runners won’t work reliably (dynamic IPs). Use a self-hosted runner or another CI environment with a stable egress IP that you can add to the allowlist.
Docs
- IP allowlist: IP allowlist
- Connect to Cloud: Connect to Cloud
If you’d like, share:
- Whether prefect cloud ip-allowlist ls shows entries/enabled
- How you’re authenticating (env vars vs prefect cloud login)
- The PREFECT_API_URL you’re using (with IDs redacted)
…and I can help you zero in on the fix. If the allowlist is enabled unexpectedly on your Starter account and you can’t disable it, we can loop in Prefect Support to remove it on the backend.