ajolopy deploy¶
Spec
Full specification: specs/cli-deploy.md
· Item: AJ-37 · Per-target follow-ups: AJ-42 / AJ-43 / AJ-44 / AJ-45.
Purpose¶
ajolopy deploy <target> emits the per-target deploy manifests your
project needs to ship. v0.1 covers five targets behind a single
command:
| Target | Manifest(s) emitted |
|---|---|
docker |
Dockerfile.prod + .dockerignore. Works on k8s / VPS / ECS / on-prem / any container runtime. Reference implementation. |
fly |
fly.toml. Generates the manifest and prints the fly launch / fly deploy flow (AJ-42). |
railway |
railway.json. Generates the manifest and prints the railway login / railway link / railway up flow for you to run. |
render |
render.yaml. Generates the Blueprint Render's dashboard applies; users commit/push the file and apply it from https://dashboard.render.com/blueprints. |
vercel |
vercel.json after an interactive warning gate about Vercel's limitations for Python AI apps. Pass -y / --yes to skip the prompt. |
The command does not invoke docker build, flyctl deploy, or any
platform CLI in v0.1 — it generates the manifests and prints the next
steps you run yourself. That keeps tests hermetic and makes the
command safe to run anywhere.
Reach for ajolopy deploy when you are ready to ship: every project
generated by ajolopy new is one ajolopy deploy <where> away from a
deployable artifact.
Signature¶
Arguments and flags¶
| Argument / flag | Type / values | Default | Description |
|---|---|---|---|
target |
docker | fly | railway | render | vercel |
required | Which deploy target to render. --help lists every registered target with its short description. |
--out PATH |
path | cwd | Project root to write manifests into. Must exist and be a directory. |
--dry-run |
flag | off | Print manifests to stdout with a # <path> header per file. Writes nothing. |
--force |
flag | off | Overwrite existing manifest files. Without it, the command refuses with a hint. |
-y, --yes |
flag | off | Skip interactive confirmation prompts (forward-compatible with AJ-45's Vercel warning gate). |
Exit codes¶
| Code | Constant | Meaning |
|---|---|---|
0 |
EXIT_OK |
Manifests written (or --dry-run succeeded). |
1 |
EXIT_USER_ABORT |
User declined an interactive prompt (Vercel warning gate). |
2 |
EXIT_USAGE |
Unknown target, missing positional, files exist without --force, or --out points at a non-directory. |
3 |
EXIT_INTERNAL |
Target rendering failed (template error, IO error, malformed pyproject.toml). |
Quick example¶
The shortest path is the docker reference target — it works on any
machine without a platform account:
$ ajolopy deploy docker
Wrote:
/workspace/acme-support/Dockerfile.prod
/workspace/acme-support/.dockerignore
Next steps:
docker build -f Dockerfile.prod -t acme-support:latest .
docker run -p 3000:3000 --env-file .env acme-support:latest
To preview without writing:
$ ajolopy deploy docker --dry-run
# Dockerfile.prod
# syntax=docker/dockerfile:1.7
...
# .dockerignore
__pycache__/
*.py[cod]
...
To overwrite an existing file:
The cloud targets all generate their platform-specific manifest and print the next-step commands for you to run. For example:
$ ajolopy deploy railway
Wrote:
/workspace/acme-support/railway.json
Next steps:
railway login
railway link
railway up
The vercel target ships an interactive warning gate before it writes
the manifest:
$ ajolopy deploy vercel
Vercel for Python AI apps has serious limitations:
- Serverless function timeout: 300s max (Pro plan)
- No persistent in-process state (in-memory Memory backends fail)
- Cold starts can break long SSE streams
If your app has:
- Workflows > 5 min -> use Fly.io or Railway
- Persistent in-proc memory -> use Fly.io or Railway
- Long streaming -> use Fly.io or Railway
Vercel works well for:
- Single-turn short agents
- Batch endpoints
- Non-streaming APIs
Continue with Vercel? (y/N) y
Wrote:
/workspace/acme-support/vercel.json
Next steps:
vercel login
vercel link # link to an existing project
vercel deploy --prod # production deploy
Pass -y / --yes to skip the prompt in scripted runs. Declining the
gate exits EXIT_USER_ABORT (1) without writing anything.
Escape hatches¶
- Custom targets. Implement the
DeployTargetprotocol and callregister_target(MyTarget())at import time. The registry uses last-write-wins semantics — that is howAJ-42/AJ-43/AJ-44/AJ-45will replace the in-tree stubs once their implementations land. - Programmatic invocation. The
_commandhelper takes anargparse.Namespaceand explicitstdout/stderr/cwdstreams; tests use it without going through the console-script wrapper. - Pyproject-driven project name.
[project] nameinpyproject.tomlbecomes the Docker image tag. Without apyproject.toml, the command falls back to the project directory name.
Common gotchas¶
- The command does not invoke
docker build,flyctl, or any other platform CLI. v0.1 stops at generating manifests and printing next steps. Run the printed commands yourself. - A second invocation against the same project fails fast unless you
pass
--force(the command refuses to overwrite without explicit consent). The original files are untouched. --outmust point at an existing directory. Pass--out /tmp/scratchto render against a clean slate during demos.- The
dockertarget reads the current Python interpreter'ssys.version_infofor the Dockerfile base image. Pinning your project to a different version is a follow-up (no--python-versionflag in v0.1 — callrender_dockerfile(python_version=...)directly from your own script if you need it). - The
verceltarget is the only v0.1 target that prompts the user insideprepare(). The injectablestdin/stdoutonVercelTargetkeep tests hermetic; pass-yto skip the gate in CI or scripted flows.
See also¶
- Quickstart — generate a project with
ajolopy newbefore deploying it. Dockerfiletemplate (AJ-41) — the pure renderers that thedockertarget wraps.- Observability recipes — once your container is running, plug Langfuse / Sentry / Grafana / Honeycomb / Datadog into the standard OTel exporter.
- Spec:
specs/cli-deploy.md.