Let Mistral host the agent: agents deploy
agents deploy deploys your agent with Studio, and Mistral hosts it: sessions survive restarts and
long waits, with nothing for you to keep running. Mistral builds your project from git, runs it, and
keeps it up.
You need:
- an agent that
agents servecan load, with an explicitname=; - a GitHub repository that holds the project, and the GitHub connector connected on your account in Mistral Studio: the platform clones the project through it;
- a
MISTRAL_API_KEYwhose user has a workspace role with Workflows access: Developer, Developer Contributor, Workspace Contributor, Observability Viewer or Admin. If yours has none, ask a workspace admin to give you one in the admin console: Organization > Workspaces > your workspace > Members. An agent with aMistralSandboxalso needs thecloud_userrole: see Mistral sandbox.
Deploy the agent
agents deploy builds the commit you pushed, not your working copy, and it builds your project's
Dockerfile. You do not have to write it: the first deploy offers to.
-
Commit and push the project.
-
Run
agents deployin the project. In a uv project with noDockerfile, it offers to write one at the project root, and a.python-versionset to3.12if the project has none. Nothing is written until you agree, and the deploy then stops:Output /home/ada/my_agent has no Dockerfile and no .python-version. Write Dockerfile and .python-version? [Y/n]: yWrote Dockerfile and .python-version in /home/ada/my_agent.Commit and push them with pyproject.toml and uv.lock, then runagents deployagain. -
Commit and push the new files, then deploy again:
git add Dockerfile .python-version pyproject.toml uv.lock && git commit -m "Add the Dockerfile" git push agents deployThe deploy first shows what it builds: the repository, the commit, the project's directory in the repository, the Dockerfile and the
mistralai-agentsrelease. It then waits until the agent is up, and says how long it took and where to try the agent:Output Git configuration┌───────────────────┬──────────────────────────────────────────┐│ Repository │ https://github.com/ada/my_agent ││ Revision │ 7253dcd591793d4baaa9338970984320cd51d5c6 ││ Working directory │ (root) ││ Dockerfile │ Dockerfile ││ Agents SDK │ 1.1.0 │└───────────────────┴──────────────────────────────────────────┘Deploying hello-agent to managed platform...Created agent 01a1a2bc-d73c-706d-a14e-11ba3b7235da (managed deployment: 01a1a2bc-d6a6-763c-9fdf-cbba1250ed5a).Agent hello-agent is live after 1m24s.Try the agent:• in Vibe Code Web: https://chat.mistral.ai/managed-agents/hello-agent• programmatically, using the Session API: https://docs.mistral.ai/studio/agents/learn/sessions/sessions -
Try the agent by its name:
agents chat hello-agentOutput Talking to hello-agent (session 01a1a89e-c09f-7043-85a7-af2170ff112d). Ctrl+D or /exit to quit.> What is the weather in Paris?⎣ doneIt's currently 18°C and sunny in Paris! ☀️
In your app, open sessions with agents.Agent.load_from(agent_name="hello-agent"), as for a served
agent. --name overrides the name= on your Agent, and is what the deployment is keyed on.
The generated Dockerfile
# Written by `agents deploy`. It is yours to edit: deploys build it as it is.
FROM ghcr.io/astral-sh/uv:0.11-debian-slim
# The worker's Temporal client reads the system CA certificates, which the base image does not ship.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# uv installs the Python version the project asks for, so the image needs none of its own. It goes
# outside /root so that the worker's user can run it.
ENV UV_COMPILE_BYTECODE=1 \
UV_PYTHON_INSTALL_DIR=/opt/python \
PATH="/app/.venv/bin:$PATH"
WORKDIR /app
COPY . .
RUN uv sync --frozen --no-dev \
&& useradd --create-home --shell /usr/sbin/nologin agent \
&& chown -R agent:agent /app
# The worker does not need root.
USER agent
CMD ["agents", "run-worker"]The image runs the Python version in .python-version.
Without a terminal, for example in CI, the deploy writes nothing and stops. Pass --yes to write the
files without the question.
The deploy stops if the Dockerfile is not in the commit you pushed, because that commit is what the
platform builds. For the generated Dockerfile, pyproject.toml, uv.lock and .python-version must
be in it too.
Hardened deployments
An agent that acts on behalf of the user who starts a session, for example through its
connectors, is deployed hardened by default: the deployment accepts
only a worker whose credential is authorized for it, so no other worker can take sessions that act
with a user's identity. Don't pass --no-hardened for such an agent: its worker cannot register,
and the deployment does not start. Hardening is fixed for the life of the deployment: to change it,
run agents shutdown <name> and deploy again. See agents deploy.
When the agent fails to start
If the deployed agent fails to start, agents deploy exits with the rollout error and prints its
last output, such as a traceback or a missing dependency:
To read the full logs, pass the agent's name to agents logs. Add --follow to keep printing new
lines:
agents logs hello-agent --followSee Agent lifecycle.
Bring your own Dockerfile
This is for projects that need more than the generated file. The generated Dockerfile is yours: later deploys build it as it is, and never write it again. Edit it to add system packages or build steps.
To build another file, pass its path, relative to the project:
agents deploy --dockerfile deploy/worker.DockerfileIf that file does not exist, the deploy stops before creating anything. The deploy only writes a Dockerfile at the default path.
A project without pyproject.toml or uv.lock gets no generated file. Write your own Dockerfile, or
create the project with uv init and uv lock.
COPY . . takes everything in the build context, so add a .dockerignore beside the Dockerfile:
.env
.venv/
.git/
__pycache__/
*.py[oc]
build/
dist/The first line matters most. An image layer keeps whatever it copied, so a .env that lands in the
image is readable by anyone who can pull it.
With the generated Dockerfile, the deployed agent runs the mistralai-agents release that the
committed uv.lock installs, not the release of the CLI that runs agents deploy. The deploy shows
that release in the Agents SDK row of its table, and warns when the two are different. To deploy
with the locked release, run uv run agents deploy in the project. With your own Dockerfile, the
deploy cannot know the release, and shows it as unknown.
The container command
The container must run only the worker, because agents deploy already registered the agent.
agents run-worker does this:
CMD ["agents", "run-worker"]Pass secrets to the deployed agent
To deploy an agent that declares secrets, first create each secret in
the deployment's workspace, at console.mistral.ai → Secrets.
--secret-binding points at an existing secret.
Then bind each MistralSecret() that does not name a secret:
agents deploy . --name hello-agent \
--secret-binding EXAMPLE_TOKEN=secret:workspace:example-token--secret-binding takes a reference, secret:workspace:<NAME>, never the secret's value.
Mistral reads the secret inside the deployed agent's container when it starts, so the value is never
typed on the command line, sent with the deploy request, or stored with the deployment. The deploy
rejects a value passed instead of a reference.
--secret-binding also accepts a name that Agent(secrets=...) does not declare, such as a
variable that a library reads rather than your tools. The deployed agent gets it from the workspace
secret the same way, but nothing checks that the agent needs it. For example, the Sentry SDK reads
SENTRY_DSN:
agents deploy . --name hello-agent \
--secret-binding EXAMPLE_TOKEN=secret:workspace:example-token \
--secret-binding SENTRY_DSN=secret:workspace:sentry-dsnEach deploy replaces the bindings of the previous one. Pass the same --secret-binding options on
every deploy: a deploy without the binding of a MistralSecret() fails. Keep them in the script or CI
job that runs the deploy.
Each --secret-binding is also passed to the image build as a build arg. A Dockerfile reads it
with a matching ARG.
Build args are not a secure channel
A value passed to the build stays in the image's history and layers, and anyone who can pull the image can recover it. Read the variable at run time instead, where it is never written to a layer. Only bind a secret at build time if you are willing to rotate it.
Session limits
By default, a deployed session ends on its own after 4 hours idle, or after 24 hours in total. Set
other limits with ENV in the Dockerfile, up to 24 hours idle and a week in total:
ENV AGENTS_SDK_IDLE_TIMEOUT_SECONDS=3600
ENV AGENTS_SDK_LIFETIME_LIMIT_SECONDS=28800A total limit of a week also needs AGENTS_SDK_EXECUTION_TIMEOUT_SECONDS raised, for example to
608400 (a week and an hour): see
Session limits.
To end a session sooner, cancel it: see Session lifecycle.
See your sessions and traces
A deployed agent's sessions are in
Managed Agents › Agent sessions in
Mistral Studio. A deployed agent also sends telemetry about agent turns, model calls and tool calls
to Mistral Studio, as OpenTelemetry spans. It is on by default, with prompt and completion
content, and needs no extra package or setting. This holds for an agent you run with agents serve
too.
Content passes through redaction before it leaves the agent's process, which masks secrets and personal data. Redaction matches known patterns, so it can miss a secret or personal data in another format.
To record spans without content, or no spans at all, set ENV in the Dockerfile. A managed deploy
does not read the caller's shell:
# Record spans without prompt and completion content.
ENV AGENTS_SDK_STUDIO_TRACING_INCLUDE_CONTENT=0
# Turn Studio tracing off entirely.
ENV AGENTS_SDK_STUDIO_TRACING_ENABLED=0An organization that disabled observability in Mistral Studio gets no traces, whatever the agent sets.
The deployed agent also reports the tokens each model call used to Mistral, without content.
ENV AGENTS_SDK_TELEMETRY_ENABLED=0 turns that report off.
Custom redaction
To decide what your agent redacts, pass redaction= to the Agent: a policy from
mistralai.agents.redaction, or a list of them, applied in order. It replaces the default policy:
import re
from mistralai import agents
from mistralai.agents import redaction
TICKET_ID = re.compile(r"\bACME-\d+\b")
agent = agents.Agent(
model="mistral-large-4",
name="support",
redaction=redaction.RegexRedactionPolicy(
# The default patterns, without card numbers, plus your own.
[*redaction.DEFAULT_TOKEN_PATTERNS, redaction.EMAIL_PATTERN, redaction.IPV4_PATTERN, TICKET_ID]
),
)redaction=[] turns redaction off. The policies, and a (key, value) function as a policy, are in
the Agent reference.
Next steps
- Agent lifecycle: try the deployed agent, read its logs, update it and shut it down.
- Session lifecycle and listing: open, cancel and list its sessions from your app.