<@ULVA73B9P> I've setup my deployments using `pref...
# ask-marvin
r
@Marvin I've setup my deployments using
prefect.yaml
, in pull-steps I've defined how to install dependencies using uv. Hence I have my
.venv
that contains all the required deps. My worker is running in a docker container as a
process
worker type But when the worker starts the flow, it seems it uses the global python interpreter, not the one from the venv because the worker told me that modules were not found while running the flow What's the best way to resolve this ? Can I specify the python interpreter path for the worker ? Do I need to move my packages from the venv to the global environment ?
m
thought for 602.4 seconds
You're running into a common gotcha with process workers: a process worker launches your flow run as a subprocess using the same Python interpreter as the worker by default. Your pull steps can create a
.venv
, but unless you explicitly switch to that interpreter, the flow will still run with the worker’s Python — hence “module not found”. There isn’t a dedicated “python executable” setting on the process worker. You have three good options: Option A — Start the worker from the venv (simplest) - Build your Docker image with your dependencies pre-installed (either system-wide or in a venv), then start the worker using that interpreter. - For example, run the worker as
.venv/bin/prefect worker start -p <your-pool>
or activate the venv before starting the worker in your container entrypoint. - This ensures the auto-generated command uses the venv’s Python. Option B — Wrap the flow run command to activate your
.venv
(works with pull steps) - Customize your work pool’s base job template so the flow run command first activates the venv your pull steps created, then runs Prefect’s generated command. - Steps: 1) Export the default process template:
Copy code
prefect work-pool get-default-base-job-template --type process > process.json
2) Edit process.json: change the job_configuration.command to something like:
Copy code
"command": "bash -lc 'source .venv/bin/activate && {{ command }}'"
Keep the rest unchanged. 3) Update the pool:
Copy code
prefect work-pool update "<your-pool>" --base-job-template process.json
- Also set the working_dir for the deployment (or pool) to the directory where
.venv
is created by your pull steps so the activation path resolves, e.g.:
Copy code
work_pool:
    name: <your-pool>
    job_variables:
      working_dir: /app  # wherever your repo is cloned and .venv lives
- On Windows containers, use:
Copy code
"command": "cmd /c \".\\.venv\\Scripts\\activate && {{ command }}\""
Option C — Use containerized runs (best isolation per deployment) - If you want fully isolated environments per deployment, switch to a Docker or Kubernetes work pool and specify an image per deployment. That way each flow run has its own environment independent of the worker. Notes: - You do not need to move packages from the venv to the global environment. Either run the worker inside the environment with the deps (Option A), or activate the venv before the flow command (Option B), or run in isolated containers (Option C). - While you can try to tweak PATH via env vars, the process worker typically builds a full interpreter path for the flow run command, so the PATH alone won’t reliably switch interpreters. Wrapping/activating the venv or running the worker within it is more robust. If you can share: - The relevant part of your prefect.yaml (pull steps + work_pool/job_variables) - Where
.venv
is created relative to your project - Your container OS (Linux/Windows) …I can suggest the exact command/template to drop in.
r
@Marvin I cannot start the work from the venv since I build it dynamically during the pull-steps. I've edited the command of my work pool to set it to
"bash -lc 'source .venv/bin/activate && {{ command }}'"
but the worker instantly ran into FileNotFoundError: [Errno 2] No such file or directory: "bash -lc 'source .venv/bin/activate && {{ command }}'"
m
thought for 371.7 seconds
Thanks for the details — that helps. Two things are happening here: 1) Process workers don’t spawn your flow in a shell; they exec a command. If you set the command to a single string that includes spaces, the worker needs to split it into argv; if the first token isn’t found, you’ll get a FileNotFoundError. In your case, the error strongly suggests the container doesn’t have bash available (very common on slim/alpine images), so “bash …” can’t be executed. 2) Even if bash were installed, using source with sh won’t work (source is a bashism). For portability, use sh and the POSIX-compatible dot builtin: . path/to/activate. A working pattern for process workers - Keep using pull steps to create .venv in your project directory. - Don’t move packages to the global env. - Customize the process work pool command to use sh and source the venv, then run the Prefect-generated command. - Ensure working_dir points to where .venv is created. Example 1) Get the default template:
Copy code
prefect work-pool get-default-base-job-template --type process > process.json
2) Edit process.json, set job_configuration.command to:
Copy code
"command": "sh -lc '. .venv/bin/activate && {{ command }}'"
Notes: - Use sh, not bash (unless you’ve installed bash in your image). - Use . .venv/bin/activate instead of source … for POSIX sh. - Keep {{ command }} intact to inject Prefect’s generated flow-run command. 3) Update your work pool:
Copy code
prefect work-pool update "<your-pool>" --base-job-template process.json
4) In your prefect.yaml, make sure runs happen in the directory that contains .venv (set working_dir accordingly):
Copy code
work_pool:
  name: <your-pool>
  job_variables:
    working_dir: /app   # adjust to wherever your repo is pulled and .venv lives
Alternative: wrapper script (if quoting is painful) - In a pull step, create an executable script runflow.sh in the repo:
Copy code
#!/bin/sh
set -e
. .venv/bin/activate
exec "$@"
- Then set the work pool command to:
Copy code
"command": "sh -lc './runflow.sh {{ command }}'"
If you’d prefer full isolation per run (no shell tricks), switch to a Docker or Kubernetes work pool and pin an image per deployment — that guarantees the interpreter and dependencies for each flow run. If you share: - Your base image (many slim/alpine images don’t include bash) - The path where pull steps create .venv - Your current working_dir/job_variables …I can tailor the exact JSON and YAML you can drop in.
r
@Marvin I've set the command to be
bash -lc "source .venv/bin/activate && {{ command }}"
. The pull-steps ran without showing me an error but the flow isn't run and the flow run state is crashed. In the UI, I just see :
Copy code
Reported flow run '7029fd06-6ed4-4d2e-94a3-a423ba46b046' as crashed: Flow run process exited with non-zero status code 1.
prefect.flow_runs.runner
Process for flow run 'horned-chimpanzee' exited with status code: 1
Without any more information
m
thought for 102.8 seconds
Thanks — the “exited with status code 1” with no logs is almost always because the custom shell command failed before Prefect could start the flow runner. Two likely culprits: - The shell you’re invoking isn’t available in your image (bash missing), or - The
.venv
path isn’t found from the working directory used to run the command Here’s how to fix and get visibility. 1) Use sh instead of bash and POSIX “.” to activate Many base images don’t include bash. Switch your work pool command to sh and use “.” instead of source:
Copy code
sh -lc '. .venv/bin/activate && {{ command }}'
2) Ensure the working_dir is where .venv lives If your pull steps create
.venv
in the cloned repo root, make sure the job runs from that directory. In your prefect.yaml, set:
Copy code
work_pool:
  name: <your-pool>
  job_variables:
    working_dir: /app   # change to wherever your repo is pulled and .venv is created
If you’re not sure where the repo ends up at runtime, add the debug wrapper below first to print
pwd
and
ls
. 3) Prefer a small wrapper script to avoid quoting issues (recommended) Have a pull step create a script that activates the venv and execs the Prefect command: - Pull step to create the script:
Copy code
echo '#!/bin/sh
set -e
echo "PWD=$(pwd)"
ls -la
if [ -f .venv/bin/activate ]; then
  echo "Activating .venv in $(pwd)"
  . .venv/bin/activate
else
  echo "ERROR: .venv/bin/activate not found"; exit 1
fi
echo "Python: $(which python)"
python -c "import sys; print(sys.executable); print(sys.version)"
exec "$@"' > runflow.sh
chmod +x runflow.sh
- Set your work pool command to:
Copy code
sh -lc './runflow.sh {{ command }}'
This makes failures obvious in logs and avoids shell-quoting pitfalls. 4) Turn up logging for more clues Set an env var on the worker or in the work pool/job variables:
Copy code
PREFECT_LOGGING_LEVEL=DEBUG
That will surface more in worker logs if something fails early. 5) Quick checklist - Is bash actually present? If you insist on bash:
apk add bash
(alpine) or
apt-get install -y bash
(debian/ubuntu), or just use sh as above. - Does
.venv
exist in the runtime working dir? If not, either fix working_dir or adjust your activation path. - Verify your current base job template:
Copy code
prefect work-pool get-default-base-job-template --type process
prefect work-pool inspect "<your-pool>"
If you share: - Your container base image - The exact path where your pull steps create
.venv
(relative to repo root) - Your current work_pool.job_variables.working_dir …I can provide the exact template/command you can copy-paste.
r
@Marvin It is really strange because the flow crashed quasi instantly while continuing to run pull-steps
m
thought for 167.6 seconds
That symptom makes sense: your shell wrapper failed before Prefect could start the runner, so the flow run “crashed” immediately, while the worker kept streaming logs from the still-running pull steps. Two key issues to address: - Don’t rely on a shell in the command string (process worker likely execs the command without a shell). - Ensure the venv is activated only after pull steps finish, from the correct working directory. The most reliable pattern is a tiny wrapper script created during pull steps and executed as the job command, with the Prefect-generated command passed via an environment variable. Do this: 1) Add a pull step to create a wrapper script Make sure it’s created in the same directory as your repo and .venv. Example:
Copy code
# In your prefect.yaml pull steps
- run:
    command: |
      cat > runflow.sh <<'EOF'
      #!/bin/sh
      set -e
      echo "PWD=$(pwd)"
      ls -la
      if [ ! -f .venv/bin/activate ]; then
        echo "ERROR: .venv/bin/activate not found"; exit 1
      fi
      # Activate venv
      . .venv/bin/activate
      echo "Using python: $(which python)"
      python -c "import sys; print('Interpreter:', sys.executable)"
      # Execute the Prefect-generated command passed in env
      exec sh -lc "$PREFECT_INNER_CMD"
      EOF
      chmod +x runflow.sh
    cwd: /app   # adjust to where your repo is pulled
2) Set your work pool to execute the wrapper and pass the inner command via env Export the default template, edit it, and update your pool:
Copy code
prefect work-pool get-default-base-job-template --type process > process.json
Edit process.json to set the command to the wrapper and inject the inner command via env:
Copy code
{
  "job_configuration": {
    "command": "./runflow.sh",
    "env": {
      "PREFECT_INNER_CMD": "{{ command }}"
    },
    "labels": "{{ labels }}",
    "name": "{{ name }}",
    "stream_output": "{{ stream_output }}",
    "working_dir": "{{ working_dir }}"
  },
  "variables": { ... keep the rest unchanged ... }
}
Apply it:
Copy code
prefect work-pool update "<your-pool>" --base-job-template process.json
3) Ensure the working directory is correct In your prefect.yaml deployment or pool job variables, point working_dir to where your repo (and .venv/runflow.sh) exist after pull steps:
Copy code
work_pool:
  name: <your-pool>
  job_variables:
    working_dir: /app   # adjust path
Why this works - The worker runs a single executable (“./runflow.sh”), which exists after pull steps. - The script activates .venv and then runs the Prefect-generated inner command (via $PREFECT_INNER_CMD). No fragile quoting in the job template and no dependency on bash; it uses sh, which is universally present. - If anything goes wrong (no .venv, wrong directory), you’ll see explicit error logs from the script instead of a silent status 1. Optional diagnostics - Set
PREFECT_LOGGING_LEVEL=DEBUG
on the pool or worker to surface more detail in logs. - If your base image is Alpine or slim, avoid relying on bash; sh is safer. If you must use bash, ensure it’s installed. If you share: - Your base image - The path where
.venv
is created (relative to the repo) - Your current
working_dir
…I can tailor the exact pull steps and process.json you can paste in.