Introducing multi-service deployments via Serverless Framework Compose

Apr 20, 2022

We are excited to announce Serverless Framework Compose: a new feature enabling you to deploy multiple services in one command, in parallel, or ordered by dependencies!

To use this new feature, upgrade the serverless CLI to v3.15 or greater and follow the guide below.

Composing multiple Serverless Framework services

Deploying multiple services in a monorepository is a very common pattern across larger teams. Orchestrating these deployments can become painful.

Let's take the following (simple) example project:

my-app/
  products/
    src/
    serverless.yml
  orders/
    src/
    serverless.yml

Our application contains 2 services. Each service can be deployed separately by running serverless deploy in each folder.

With Serverless Framework Compose, it is possible to deploy all those Serverless Framework services via a single serverless deploy command. To do so, create a serverless-compose.yml file at the root:

my-app/
  serverless-compose.yml
  products/
    src/
    serverless.yml
  orders/
    src/
    serverless.yml

The new "serverless-compose.yml" configuration file references the existing Serverless Framework projects:

# serverless-compose.yml
services:

  products:
    path: products

  orders:
    path: orders

As you can see above, each existing Serverless Framework service is referenced via a relative path. When running "serverless deploy" in the root directory, Compose will deploy both services ("products" and "orders") in parallel.

While Compose triggers the deployment, each deployment runs as usual, in the exact same conditions as a traditional Serverless Framework deployment. This was designed so that introducing Compose in an existing project would have no side-effect.

Note that it is also possible to deploy all services to a specific stage with the --stage option.

Solving service dependencies

Sometimes, services have dependencies between them. The output of one service (for example its API URL, a table name, a queue URL, etc.) might be used by another.

Passing outputs between dependencies no longer requires complex solutions with CloudFormation imports. In the serverless-compose.yml file, you can now pass outputs from one service to another:

# serverless-compose.yml
services:

  products:
    path: products

  orders:
    path: orders
    params:
      productsTableName: ${products.tableName}

Let's break it down into 3 steps:

  1. ${products.tableName} will resolve to the "tableName" output of the "products" service.

    The outputs of a Serverless Framework service are taken from its CloudFormation outputs. Here is how we can expose the "queueUrl" output in the products/serverless.yml config:

    # products/serverless.yml
    ...
    
    resources:
      Resources:
        Table:
          Type: AWS::DynamoDB::Table
          ...
    
      Outputs:
        tableName:
          Value: !Ref Table
    
  2. Because of the dependency introduced by the variable, serverless deploy will automatically order deployments and deploy "products" first, and then "orders".

  3. The value will then be passed to the "orders" service as a parameter named "productsTableName". Parameters can be referenced in Serverless Framework configuration:

    # orders/serverless.yml
    provider:
      ...
      environment:
        # Inject the value in a function environment variable
        PRODUCTS_TABLE: '${param:productsTableName}'
    

Compose is designed to integrate with existing serverless.yml features (CloudFormation outputs for outputs, Serverless Framework parameters for inputs). That way, using Compose does not require a huge rearchitecture of existing monorepositories.

Setting dependencies without variables

Using variables allows us to exchange values and order deployments. It is also possible to order deployments without variables via the "dependsOn" feature:

# serverless-compose.yml
services:

  products:
    path: products

  orders:
    path: orders
    dependsOn:
      - products

In the example above, the "products" service will be deployed before "orders". This can be useful in scenarios where a service interacts with another and we want to make sure API or schema changes are deployed in a specific order.

Global commands and service commands

An interesting benefit of grouping multiple services behind a single configuration is that we can run one command in all services at once. For example:

We can also run service-specific commands without having to change directories:

What's next

Serverless Framework now lets you compose services to orchestrate their deployments.

Services can integrate together via variables and are deployed in order based on their dependencies.

There is a lot more to Serverless Framework Compose, head over to the documentation to learn more and get started.

We are also exploring exciting ideas to improve Compose in the near future, like:

  • accelerating deployments for services that haven't changed
  • deploying only specific services via a filter flag
  • multi-region deployments

Get involved in the Serverless Framework repository to discuss these ideas!

Subscribe to our newsletter to get the latest product updates, tips, and best practices!

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.