Blog
Guides

Deploy from a monorepo or subfolder, with system packages

DK
Dan Kowalsky
Founder ·

Callables builds your repo the way you already set it up. Most projects need nothing extra. This guide covers the settings for everything else: automations in a subfolder, a specific Python version, and command-line tools your code shells out to.

Dependencies

Callables installs your Python dependencies from requirements.txt at the repo root, or from [project].dependencies in pyproject.toml if there's no requirements.txt.

Don't list callables itself. The platform installs its own copy, matched to the server.

Python version

The version comes from, in order:

  1. A .python-version file, like 3.13.
  2. Otherwise, the newest version your pyproject.toml requires-python allows.
  3. Otherwise, Python 3.12.

Python 3.11 to 3.14 are supported.

Automations in a subfolder

By default, the whole repo is built. If your automations live in one folder of a bigger repo, add a callables.toml at the repo root:

root = "automations"

Or put the same setting in pyproject.toml:

[tool.callables]
root = "automations"

Then only that folder is built, and its own requirements.txt, apt.txt and .python-version are used. Code inside it can't import from the rest of the repo, so keep shared helpers inside the folder. call check and call local use the same folder when you run them from the repo root.

System packages with apt.txt

When a function shells out to a command-line tool, like a converter, a database client or a headless browser, list the Debian packages it needs in apt.txt:

# apt.txt: one package per line
ffmpeg
poppler-utils
postgresql-client
  • Names only, no versions. You get the version Debian 13 ships.
  • Packages are installed without their "recommended" extras. If a tool is missing something, like fonts, list that package too.
  • They're installed once each time apt.txt changes, not on every push.
  • call local uses the tools on your own machine, so install them there too.

The runtime

A few things to know about where your code runs:

  • Your code is read-only. Write temporary files to /tmp (up to 256 MB) or your home directory.
  • Every run starts fresh. Nothing persists between runs, so keep state in your own database or storage.
  • Downloaded binaries can't run from /tmp. Put them in your home directory and make them executable, or better, install the tool with apt.txt.
  • Memory and CPU go up to your plan's limit, and you're billed for what the run actually uses. Each run's page shows its peak memory and CPU time.

Turn your scripts into tools your team can run

Add @deploy to a Python function and push. Your team gets a form, schedules and run history.

See how it works