What the agent does
In a repository like docker/awesome-compose, every image reference is a moving target or a stale one, and the agent starts by listing them. react-express-mongodb/compose.yaml runs mongo:4.2.0 and builds two services whose Dockerfiles start FROM node:lts-buster-slim and node:lts-buster. nginx-flask-mysql/compose.yaml runs mariadb:10-focal, its backend Dockerfile starts FROM python:3.10-alpine and its proxy Dockerfile FROM nginx:1.13-alpine. django/app/Dockerfile starts FROM python:3.7-alpine. The agent records each one with the file and line it came from, because a tag in compose.yaml and a FROM line in a Dockerfile are upgraded in different places.
For each image it looks up the newest tag the stack can take and the digest that tag points to, then writes the pin as image:tag@sha256:digest. It reads what the application inside expects before choosing: the Express backend's package.json asks for mongoose ^6.0.9 and mongodb ^3.0.7, so a MongoDB server major has to be one those drivers speak; the Flask backend pins Flask==2.0.1 and mysql-connector==2.2.9 in requirements.txt; the Django app pins Django==3.2.13, which limits how far the Python base image can move. A base image bump that needs an application dependency bump is reported as such instead of being forced.
Then it proves each stack. docker compose config renders the merged file and catches a typo in an image reference or a removed key. docker compose up --wait builds the images and waits for every service to be running, and for a service with a healthcheck, healthy. Only nginx-flask-mysql defines one (mysqladmin ping on the db service, with the backend waiting on condition: service_healthy), so for the other two the agent also makes a request: the Django runserver on port 8000, and the backend on the express-mongo network once Mongo is up. Logs from any container that exits go into the run's record.
Every bump is tried on its own, so a failure has one cause. If node:lts moving to a newer Debian base breaks npm ci in the frontend build, that bump is left out and the reason, with the failing command's output, goes into the pull request. The stacks it was not asked to touch stay as they are.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Pin and upgrade the container images in the react-express-mongodb, nginx-flask-mysql and django stacks only. For each image in compose.yaml and each FROM line in their Dockerfiles, choose the newest tag the application's own dependencies support and pin it as tag@sha256 digest. Try each bump on its own: run docker compose config, then docker compose up --wait, and for services without a healthcheck make one request that proves the app answers. Keep the bumps that pass, leave out any that break the build or the stack and say why with the command output, and open one pull request with one line per bump: file, old reference, new reference, and how it was proven.",
"resources": [
{
"url": "https://github.com/your-org/your-app"
}
]
}- Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
- Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
- Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.
What comes back
The pull request changes only compose.yaml files and Dockerfiles in the three stacks it was given. Its description is a table of bumps, one line each, with the old reference, the pinned reference and the command that proved it, followed by a list of upgrades it left out and why. A digest pin means the same bytes run in CI, on a laptop and in production until the next upgrade changes it on purpose.
A reviewer checks three things before merging: that each tag is a release the application supports (a MongoDB or MariaDB major is a data format change, not only a version string), that the left-out list names a real failure rather than a guess, and that the run's transcript shows docker compose up --wait finishing for each stack. The transcript is GET /o/{org}/sessions/{id}/messages, every command with its output.
Run it on every Monday morning
Image tags move without a commit in your repository, so this job is worth repeating. Have a scheduled job send the same POST /o/{org}/sessions body each week with an Idempotency-Key that includes the date, so a retried request does not start a second run, and give the API key a webhook URL so your service hears run.completed or run.failed when the turn ends.
Because every bump is pinned to a digest, the weekly pull request is small when nothing upstream moved and says exactly what changed when something did.
The use case this job follows, with its automation and trigger: Upgrade dependencies weekly.
Why run it on Rush?
Bringing three Compose stacks up and down, one bump at a time, means pulling base images, rebuilding, starting databases and waiting on healthchecks. On a laptop that occupies Docker for the whole job and leaves volumes and images behind. In Rush the work happens in the run's own cloud sandbox, and the commands it ran, with their output, stay in the session's transcript for whoever reviews the pull request.
The result is a pull request with evidence, not a list of newer tags. A bump that broke docker compose up --wait never reaches the diff; it appears in the description with the output that shows why, which is what makes a weekly upgrade pull request something people merge.
Frequently asked questions
Why pin to a digest and not only a tag?
A tag such as node:lts or mariadb:10-focal can be republished to point at new bytes. A tag with @sha256 runs the same image every time until someone changes the line. Keeping the tag beside the digest leaves the version readable for a reviewer.
Does docker compose up --wait prove the app works?
It proves every service started and, where a healthcheck exists, reported healthy. In awesome-compose only the nginx-flask-mysql database has a healthcheck, so the request asks the agent to make one call to each app as well. Adding healthchecks is a separate change it can propose but does not make unasked.
Will it upgrade MongoDB or MariaDB across a major version?
Only when the application's drivers support the new server, and the pull request says so on that line. A database major can change the on-disk format of an existing volume, so a reviewer should treat those lines as a migration, not a routine bump.
What happens to stacks I did not name?
They stay untouched. The request names three directories, and the agent changes files only there. Name more directories, or send one run per stack, to cover the rest of a large repository.