Join us for GraphQL Summit, October 10-12 in San Diego. Use promo code ODYSSEY for $400 off your pass.
Launch GraphOS Studio

Deploying with managed federation

Best practices

When rolling out changes to a subgraph, we recommend the following workflow:

  1. Confirm the backward compatibility of each change by running rover subgraph check in your CI pipeline.
  2. Merge backward compatible changes that successfully pass .
  3. Deploy changes to the in your infrastructure.
  4. Wait until all replicas finish deploying.
  5. Run rover subgraph publish to update your managed federation configuration:
rover subgraph publish my-supergraph@my-variant \
--schema ./accounts/schema.graphql \
--name accounts \

Pushing configuration updates safely

Whenever possible, you should update your configuration in a way that is backward compatible to avoid downtime. As suggested above, the best way to do this is to run rover subgraph check before updating. You should also generally seek to minimize the number of breaking changes you make to your s.

Additionally, call rover subgraph publish for a subgraph only after all replicas of that subgraph are deployed. This ensures that s are in place for all s that are executable against your graph, and operations can't attempt to access s that do not yet exist.

In the rare case where a configuration change is not backward compatible with your 's query planner, you should update your registered s before you deploy your updated code.

You should also perform configuration updates that affect query planning prior to (and separately from) other changes. This helps avoid a scenario where the query planner generates queries that fail validation in downstream services or violate your s.

Examples of this include:

  • Modifying @key, @requires, or @provides s
  • Removing a type implementation from an interface

In general, always exercise caution when pushing configuration changes that affect your 's query planner, and consider how those changes will affect your other s.

Example scenario

Let's say we define a Channel interface in one , and we define types that implement Channel in two other s:

# channel subgraph
interface Channel @key(fields: "id") {
id: ID!
# web subgraph
type WebChannel implements Channel @key(fields: "id") {
id: ID!
webHook: String!
# email subgraph
type EmailChannel implements Channel @key(fields: "id") {
id: ID!
emailAddress: String!

To safely remove the EmailChannel type from your :

  1. Perform a rover subgraph publish of the email that removes the EmailChannel type from its .
  2. Deploy a new version of the that removes the EmailChannel type.

The first step causes the query planner to stop sending s ...on EmailChannel, which would fail validation if sent to a that isn't aware of the type.

If you want to keep EmailType but remove it from the Channel interface, the process is similar. Instead of removing the EmailChannel type altogether, only remove the implements Channel addendum to the type definition. This is because the query planner expands queries to interfaces or unions into s on their implementing types.

For example, a query such as...

query FindChannel($id: ID!) {
channel(id: $id) {

...generates two queries, one to each , like so:

# Generated by the query planner
# To email subgraph
query {
_entities(...) {
...on EmailChannel {
# To web subgraph
query {
_entities(...) {
...on WebChannel {

Currently, the expands all interfaces into implementing types.

Removing a subgraph

To "de-register" a with Apollo, call rover subgraph delete:

This action cannot be reversed!

rover subgraph delete my-supergraph@my-variant --name accounts

The next time it starts up or polls, your obtains an updated configuration that reflects the removed .

Using variants to control rollout

With managed federation, you can control which version of your graph a fleet of s are using. In the majority of cases, rolling over all of your router instances to a new version is safe, assuming you've used schema checks to confirm that your changes are backward compatible.

However, changes at the level might involve a variety of different updates, such as migrating entities from one to another. If your infrastructure requires a more advanced deployment process, you can use graph variants to manage different fleets of s running with different configurations.


To configure a canary deployment, you might maintain two production graph s in Apollo Studio, one named prod and the other named prod-canary. To deploy a change to a named launches, you might perform the following steps:

  1. Check the changes in launches against both prod and prod-canary:
    rover subgraph check my-supergraph@prod --name launches --schema ./launches/schema.graphql
    rover subgraph check my-supergraph@prod-canary --name launches --schema ./launches/schema.graphql
  2. Deploy your changes to the launches in your production environment, without running rover subgraph publish.
    • This ensures that your production router's configuration is not updated yet.
  3. Update your prod-canary 's registered , by running:
    rover subgraph publish my-supergraph@prod-canary --name launches --schema ./launches/schema.graphql
    • If composition fails due to intermediate changes to the canary graph, the canary router's configuration will not be updated.
  4. Wait for health checks to pass against the canary and confirm that metrics appear as expected.
  5. After the canary is stable, roll out the changes to your production s:
    rover subgraph publish my-supergraph@prod --name=launches --schema ./launches/schema.graphql

If your canary reports metrics to GraphOS, you can use Apollo Studio to verify a canary's performance before rolling out changes to the rest of the graph. You can also use s to support a variety of other advanced deployment workflows, such as blue/green deployments.

Modifying query-planning logic

Treat migrations of your query-planning logic similarly to how you treat database migrations. Carefully consider the effects on downstream services as the query planner changes, and plan for "double reading" as appropriate.

Consider the following example of a Products and a Reviews :

# Products subgraph
type Product @key(fields: "upc") {
upc: ID!
nameLowerCase: String!
# Reviews subgraph
type Product @key(fields: "upc") {
upc: ID!
reviews: [Review]! @requires(fields: "nameLowercase")
nameLowercase: String! @external

Let's say we want to deprecate the nameLowercase and replace it with the name , like so:

# Products subgraph
type Product @key(fields: "upc") {
upc: ID!
nameLowerCase: String! @deprecated
name: String!
# Reviews subgraph
type Product @key(fields: "upc") {
upc: ID!
nameLowercase: String! @external
name: String! @external
reviews: [Review]! @requires(fields: "name")

To perform this migration in-place:

  1. Modify the Products to add the new . (As usual, first deploy all replicas, then use rover subgraph publish to push the new .)
  2. Deploy a new version of the Reviews with a that accepts either nameLowercase or name in the source object.
  3. Modify the Reviews 's in the so that it @requires(fields: "name").
  4. Deploy a new version of the Reviews with a that only accepts the name in its source object.

Alternatively, you can perform this with an atomic migration at the level, by modifying the subgraph's URL:

  1. Modify the Products to add the name (as usual, first deploy all replicas, then use rover subgraph publish to push the new ).
  2. Deploy a new set of Reviews replicas to a new URL that reads from name.
  3. Register the Reviews with the new URL and the changes above.

With this atomic strategy, the query planner resolves all outstanding requests to the old URL that relied on nameLowercase with the old query-planning configuration, which @requires the nameLowercase . All new requests are made to the new URL using the new query-planning configuration, which @requires the name .

Reliability and security

Your fetches its configuration by polling Apollo Uplink, an Apollo-hosted endpoint specifically for serving configs. In the event that your updated config is inaccessible due to an outage in Uplink, your router continues to serve its most recently fetched configuration.

If you restart a instance or spin up a new instance during an Uplink outage, that instance can't fetch its configuration until Apollo resolves the outage.

The subgraph publish lifecycle

Whenever you call rover subgraph publish for a particular , it both updates that subgraph's registered and updates the 's managed configuration.

Because your graph is dynamically changing and multiple s might be updated simultaneously, it's possible for changes to cause composition errors, even if rover subgraph check was successful. For this reason, updating a re-triggers composition in the cloud, ensuring that all subgraphs still compose to form a complete before updating the configuration. The workflow behind the scenes can be summed up as follows:

  1. The is uploaded to Apollo and indexed.
  2. The is updated in the to use its new .
  3. All s are composed in the cloud to produce a new .
  4. If composition fails, the command exits and emits errors.
  5. If composition succeeds, Apollo Uplink begins serving the updated .

On the other side of the equation sits the . The router regularly polls Apollo Uplink for changes to its configuration. The lifecycle of dynamic configuration updates is as follows:

  1. The polls for updates to its configuration.
  2. On update, the downloads the updated configuration, including the new .
  3. The uses the new to update its query planning logic.
  4. The continues to resolve in-flight requests with the previous configuration, while using the updated configuration for all new requests.
Federated schema checks
Opt-in error reporting
Edit on GitHubEditForumsDiscord