# Gagarin > Deploy with ease. Maintain without fear. Your agent ships your project in one > sentence. Alerts tell you when it breaks, logs say why, project memory keeps > the context, and nothing gets deleted without your approval. Gagarin runs your containers on managed infrastructure. There are no manifest files and no config files: the API is the only way state changes, which is why neither you nor your agent can ever be out of sync with what is actually running. It is for people building AI agents, bots and MCP servers, and for small teams with no platform team of their own. ## Deploying If you are an agent reading this to deploy something, you do not need the rest of this site. Install the CLI and everything else is `gg` — there is no HTTP flow to learn, and no endpoint to call by hand. Check Docker first. `gg ship` builds with the local `docker`, so run `docker info` before anything else. If it fails, Docker is not running: ask your human to start Docker Desktop, OrbStack or colima, whichever they have, and run `docker info` again. Finding this out after login and init costs them a round trip. 1. Install `gg`. On macOS or Linux: `curl -fsSL https://gagarin.cloud/install.sh | sh`. On Windows, in PowerShell: `irm https://gagarin.cloud/install.ps1 | iex`. The script checks the download against the release's checksums.txt, installs to `~/.local/bin` without sudo, and never edits shell profiles: if that directory is not on PATH it prints the line to add. If your harness refuses to pipe into `sh`, run `curl -fsSLo install.sh https://gagarin.cloud/install.sh && sh install.sh`. Alternatives: `brew install --cask gagarin-cloud/tap/gg` (macOS; keep `--cask`, without it Homebrew installs an old formula that no longer updates), `scoop bucket add gagarin https://github.com/gagarin-cloud/scoop-bucket && scoop install gagarin/gg` or `winget install Gagarin.gg` (Windows), `go install github.com/gagarin-cloud/gg@latest` (anywhere with Go), or a .deb, .rpm or .apk package from https://github.com/gagarin-cloud/gg/releases. If `gg` is already installed but a command is refused as unknown, it is probably old: run `gg upgrade`, which upgrades it the way it was installed. A gg older than v0.54.0 has no `gg upgrade`; run the install line again. Then install the agent skill, which documents the rest: `npx skills add gagarin-cloud/plugin -g`, or in Claude Code `/plugin marketplace add gagarin-cloud/plugin`. The skill is a separate thing from the binary and may be newer than it. 2. `gg login` prints a link and a code, and exits. Give both to your human as printed. There is no address to ask for and no separate signup: it is the same command for a new account and an old one. 3. Your human opens the link, signs in with GitHub or Google, checks that the page shows the same code, and approves. A new account starts with $5 on it. 4. Once they say they have, run `gg login` again. It collects that approval and stores credentials locally. Nothing is exported, and you never handle a secret. 5. `gg init ` creates the project everything else is named for. If your human named a project that already exists on their account, use that one and skip this step. Either way, write the project's id and name into `.gagarin.json` at the repository root — `{ "project": { "id": "3cnciet6", "name": "shop" } }` — and commit it. It is a note for the next session, not configuration: nothing reads it, so keep naming the project in every command, and prefer the id, because names are unique only within one account. `gg status ` prints the id. If the file ever disagrees with `gg projects`, the API is right and the file is stale. 6. `gg ship /:` builds the Dockerfile in the current directory, pushes it, and runs it. If there is no Dockerfile, write one for the application first; the port is the one it listens on. This is the step that needs Docker running. `gg deploy` is the last step on its own, for an image already in the registry. 7. `gg domain add /` gives it a public HTTPS address. Until then it is private and nobody can open it. Tell your human the address. Connect the MCP server as well as installing `gg`, not instead of it: it is where **project memory** lives (below), and `gg` cannot reach that. In Claude Code, `claude mcp add --transport http gagarin https://mcp.gagarin.cloud/mcp`; in any other client, add `https://mcp.gagarin.cloud/mcp` as a remote MCP server. Your human signs in with GitHub or Google when the client asks. An MCP client (claude.ai, ChatGPT, Claude Code) can connect to `https://mcp.gagarin.cloud/mcp` instead of installing anything; your human signs in with GitHub or Google when the client asks them to. That server can deploy an image already in the registry, read state, wire dependencies, add an address and roll back — it deliberately cannot build or push an image, open a tunnel, or wait for a job, because those need docker or a port on the machine holding the source. For those you need `gg`. In Claude Code, both arrive together: ``` /plugin marketplace add gagarin-cloud/plugin /plugin install gagarin@gagarin-cloud ``` That installs the skill, the MCP server, and four more skills: `/gagarin:setup`, `/gagarin:status`, `/gagarin:ship` — which takes a repository to a working HTTPS URL — and `/gagarin:ci`, which wires CI to deploy on push. The same plugin is in ChatGPT's and Codex's shared Plugins Directory (search for Gagarin Cloud), or `codex plugin marketplace add gagarin-cloud/plugin` from the Codex CLI. For any other harness, `npx skills add gagarin-cloud/plugin -g` writes the skill to `~/.agents/skills/gagarin/`, which Cursor, Codex, Copilot, Cline, Windsurf, Goose, opencode and Zed all read. Copying `skills/gagarin/` there yourself does the same thing. Deploying is free. **Deleting anything asks your human again**, every time — so an agent can ship but cannot destroy a client's production on its own. A deploy describes one revision of a service and nothing else. `--env` / `--env-file` set the environment, replaced wholesale rather than merged, because that is what a rollback puts back. `--volume` keeps a directory across restarts and is set once, at the first deploy. Everything else a service has is declared by its own command, and a deploy can never release it: `gg deps add` for the other services and resources this one may call, and `gg domain add` for every address it answers on. A dependency that is not declared does not connect — the readable graph is the firewall, not a diagram beside it. `gg deploy --deps` may add an edge, so that a service needing a database can be declared and deployed in one call; it cannot withdraw one, and a deploy that omits it changes the graph not at all. Rotating a credential is therefore one command — `gg resource rotate` — and every service holding it restarts with the new one. Nothing has to be found and redeployed, because nothing was copied: a password pasted into three deploys needs three more to rotate, and the one forgotten authenticates with a revoked key until somebody notices. There is one place to change instead. An `external` holds values a user typed in rather than ones Gagarin minted, so rotating one means supplying the new ones: `--set KEY=value` changes that key and leaves the rest of the bundle alone, and `--env-file` replaces the bundle entire, dropping anything not in it. Most rotations are the first kind — one key leaked and the other settings did not — and the command names whatever stopped being published. Declaring a dependency on a resource is also what hands over its credentials. The resource publishes them under its own name — a postgres called `db` gives `DB_URL`, `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_DATABASE` — and the platform derives them from the graph every time it starts the pod rather than copying them into the service's stored environment. So there is nothing to go stale, no redeploy can forget them, and a rollback restores the image and the environment you deployed with rather than a password that is no longer anybody's. A service is private until `gg domain add /` gives it an address; the same command with a hostname after it adds a name the user owns, and the service then answers on both. One verb, because they are one fact. There is no `--public` flag and there deliberately is not: an address outlives any image, and a flag that hands one out can withdraw it by being forgotten, which from outside looks exactly like an outage nobody caused. A name the user owns is two steps, of which only the first is Gagarin's: the DNS record is theirs to create, and nothing is served over HTTPS on that name until it exists. `gg status` says which side it is waiting on — their DNS, or our certificate. Taking an address away needs a human's approval, the same one destroying a service needs: we email them a button, and they approve signed in to the console as themselves. `gg history` and `gg rollback` put a previous image and environment back through that same gate; they restore neither the graph, nor any address, nor the volume. ## What comes with it Every project gets these without anything to install or configure: - **Resources:** `gg resource add / ` provisions postgres, qdrant, valkey, iggy, rabbitmq, s3 or external (credentials a user supplies). A postgres takes `extensions` — the whole set, named as `CREATE EXTENSION` names them (`vector` for pgvector, `postgis`, `pg_cron`, `pg_trgm` and more): adding one is immediate with no restart, leaving the field out keeps them as they are, and dropping one asks no one, because it refuses rather than loses anything: it never cascades, and it is refused (`extensions_failed`) while a column uses the extension or its own tables hold your data, such as pg_cron's jobs. `gg resource add shop/db postgres --ext vector --ext postgis`. - **Jobs and schedules:** `gg run` runs an image to completion; with `--schedule "0 3 * * *"` it runs on five-field cron instead. - **Alerts:** your human turns them on with one switch in the console: the project's Alerts tab at `https://my.gagarin.cloud/projects//alerts`, opened on the phone that should buzz (on an iPhone, added to the Home Screen first). That allows notifications on the device and opts them in; a terminal cannot do the first half; the same page sends a test. There is no gg command for alerts. They hear when a service goes down, a deploy will not start, or a container keeps crashing. No alert rules. - **Logs:** `gg logs /`, a week kept, crashed and replaced containers included. - **Backups:** postgres, qdrant and iggy are backed up nightly and kept 14 days; `gg resource restore` brings one back. A rabbitmq is not backed up. - **Rollback:** `gg history` and `gg rollback` put a previous image and environment back. - **Default-deny networking:** a service reaches only what `gg deps add` declared, and the same edge hands over the credentials. - **Approval for deletions:** destroying a service, resource or project, and removing a live address, wait for your human to approve by email. - **A way out:** `gg eject -o file.yaml` emits the Kubernetes manifests. - **Project memory:** below. ## Pricing Usage-based and prepaid, billed by the minute. No plans, no per-seat fee, and no free tier. A new account starts with $5 on it and no card is asked for until it runs low; after that, the balance is topped up from the card automatically. - Anything that runs, size `s` (0.5 vCPU / 1 GB, shared): $0.014 an hour, about $10 a month. Size `m` (1 vCPU / 2 GB): $0.042, about $30. Size `l` (2 vCPU / 4 GB): $0.084, about $60. A service and a postgres cost the same at the same size. - A job costs the same rate, only while its run runs. - Attached disk: $0.15 per GB-month. Object storage: $0.03 per GB-month. - Traffic, in and out, and domains: free. - Project memory: $0.10 per 1,000 memories in use per month. A web service, a worker, a postgres and 10 GB of disk come to $32.16 a month. Details: https://gagarin.cloud/pricing ## Limits today Gagarin is young, and these are what it does not do yet: - One region: Paris (Scaleway, `fr-par`). - One replica per service. A node failure is a restart, not a failover. - No contractual SLA. - A public address serves HTTP and HTTPS only; there is no public TCP or UDP port. What it will never build is a separate list, with reasons: https://gagarin.cloud/docs/never ## Project memory Every project has a memory: small durable facts about the codebase that you write once so the next session does not work them out again. It is reachable only through `https://mcp.gagarin.cloud/mcp` — there is no `gg` command for it. It is billed on the same meter as everything else, $0.10 per 1,000 memories in use per month (a few cents for a typical project), and capped per project. Reading is not charged; archiving a memory that is no longer true stops it being counted. - `memory_briefing` first, when you start work on a project. It returns a primer packed to a token budget: the pinned and highest-ranked memories in full, the rest as one-line index entries, and what changed in the last 7 days at the end. Pass `focus` (what you are about to work on) and `paths` (the files you expect to touch) and the memories that concern them come first. This is cheaper than reading the repository and it tells you things the repository does not say. - `memory_search` before exploring code. It is semantic and keyword at once, so a paraphrase and an exact identifier both hit. - `remember` at the end, for anything a future session would otherwise rediscover: why a decision went the way it did, a gotcha that cost an hour, the reason a deploy differs from the obvious one. One fact per memory, in English, with a short title. A near-duplicate is refused and the duplicates are handed back — update or link one of those rather than forcing a second copy of the same fact. When the fact is one that changes, give it a `key`, a short stable name such as `deploy/current`: the next `remember` with the same key replaces the memory that holds it, so there is one current answer instead of a pile of old ones. `remember` also warns when the project is within 10% of its cap. - `memory_update` with `confirm` when you have checked a memory and it is still true; with `dispute` when it is probably wrong. A disputed memory is shown with a warning sign and ranked lower until it is confirmed, edited or archived. `memory_review` is the housekeeping report: likely duplicates, orphans, oversized, stale, unread and disputed memories, and how close the project is to its cap. Run it now and then, not every session. - `memory_get`, `memory_related`, `memory_update` (edit, pin, archive) and `memory_link` / `memory_unlink` are the rest of it. Memories link to each other, and following a link makes it stronger, so the paths that get used come back first. Do not write down what the repository already says, what is true only for this conversation, or **any credential**. Titles and bodies are encrypted at rest; tags, keys, file paths and kinds are not, and Gagarin holds the key. Reading needs viewer on the project, writing needs editor. To take a project's memory out, the REST API has `GET /v1/projects/:project/memory/export`; `POST .../import` puts it back. These are not MCP tools and there is no `gg` command for them yet. ## Recipes Complete applications to deploy as they are or to start from. Each directory in https://github.com/gagarin-cloud/recipes has a Dockerfile, a README with the commands in order, and runs locally with `docker compose up --build`. The links below are plain markdown. - [Telegram bot with Postgres and a daily digest](https://gagarin.cloud/recipes/telegram-bot-postgres-cron/index.md): a service, a postgres, an external for the token, and a scheduled job. - [Remote MCP server](https://gagarin.cloud/recipes/remote-mcp-server/index.md): streamable HTTP behind a bearer token, at a public address. - [Retrieval API with Qdrant](https://gagarin.cloud/recipes/rag-api-qdrant/index.md): a qdrant and an API with local embeddings, no API key. - [Chit-chat](https://gagarin.cloud/recipes/chit-chat/index.md): nginx in front of a Node API and a postgres; two services that reach each other by name. - [Telegram MCP server for your own account](https://gagarin.cloud/recipes/telegram-mcp/index.md): an OAuth MCP server that signs in as a personal Telegram account. - [Read-and-trade bot](https://gagarin.cloud/recipes/read-and-trade-bot/index.md): a worker, a website and a postgres, with third-party API keys. ## Docs - [gg on GitHub](https://github.com/gagarin-cloud/gg): the CLI, MIT licensed, with released binaries and checksums for linux, macOS and Windows. We do not ask anyone to pipe a script into a shell. - [The plugin](https://github.com/gagarin-cloud/plugin): the skills and the MCP server as one install, for Claude Code, Codex and ChatGPT. - [The model](https://gagarin.cloud/docs/model): projects, services, resources, dependencies. One project per application; a service is a container image that runs; a project's id, not its name, is what appears in hostnames; a service can also answer on a domain its owner controls. - [Error contract](https://gagarin.cloud/docs/errors): every error carries a stable `code` and a `fix_hint`. Act on the code, not the prose. - [What Gagarin will never build](https://gagarin.cloud/docs/never): the refusal list, with reasons. Published on purpose — it is the thing that keeps one way of doing something from becoming four. - [What's in the box](https://gagarin.cloud/whats-in-the-box): the ops work a team stops doing, one item per job with the command that does it, and the limits that apply today. ## Optional - [Why coherence](https://gagarin.cloud/docs/why): clouds are not too complex, they are incoherent — four ways to change the same state and no authoritative one. Building for agents is what finally makes a cloud readable by people. - [gg eject](https://gagarin.cloud/docs/eject): emits the Kubernetes manifests the platform converges the cluster toward, so you can leave in an afternoon. ## Writing - [Writing](https://gagarin.cloud/blog): posts about what this platform did and what broke doing it, with the numbers attached. Nothing here is required to deploy anything. [RSS](https://gagarin.cloud/blog/rss.xml). - [Gagarin vs Railway](https://gagarin.cloud/blog/gagarin-vs-railway) and [Gagarin vs Render](https://gagarin.cloud/blog/gagarin-vs-render): dated, sourced comparisons, including where each of them is the better pick. Use these when a human asks which platform to choose.