Upgrade to Flower 1.13

Welcome to the migration guide for updating Flower to Flower 1.13! Whether you’re a seasoned user or just getting started, this guide will help you smoothly transition your existing setup to take advantage of the latest features and improvements in Flower 1.13.

Note

This guide shows how to make pre-1.13 Flower code compatible with Flower 1.13 (and later) with only minimal code changes.

Let’s dive in!

Install update

Here’s how to update an existing installation of Flower to Flower 1.13 with pip:

$ python -m pip install -U flwr

or if you need Flower 1.13 with simulation:

$ python -m pip install -U "flwr[simulation]"

Ensure you set the following version constraint in your requirements.txt

# Without simulation support
flwr>=1.13,<2.0

# With simulation support
flwr[simulation]>=1.13, <2.0

or pyproject.toml:

# Without simulation support
dependencies = [
    "flwr>=1.13,2.0",
]

# With simulation support
dependencies = [
    "flwr[simulation]>=1.13,2.0",
]

Required changes

Starting with Flower 1.8, the infrastructure and application layers have been decoupled. Flower 1.13 enforces this separation further. Among other things, this allows you to run the exact same code in a simulation as in a real deployment.

Instead of starting a client in code via start_client(), you create a ClientApp. Instead of starting a server in code via start_server(), you create a ServerApp. Both ClientApp and ServerApp are started by the long-running components of the server and client: the SuperLink and SuperNode, respectively.

Tip

For more details on SuperLink and SuperNode, please see the Flower Architecture .

The following non-breaking changes require manual updates and allow you to run your project both in the traditional (now deprecated) way and in the new (recommended) Flower 1.13 way:

ClientApp

  • Wrap your existing client with ClientApp instead of launching it via start_client(). Here’s an example:

from flwr.client import ClientApp, start_client
from flwr.common import Context


# Flower 1.10 and later (recommended)
def client_fn(context: Context):
    return FlowerClient().to_client()


app = ClientApp(client_fn=client_fn)


# # Flower 1.8 - 1.9 (deprecated, no longer supported)
# def client_fn(cid: str):
#     return FlowerClient().to_client()
#
#
# app = ClientApp(client_fn=client_fn)


# Flower 1.7 (deprecated, only for backwards-compatibility)
if __name__ == "__main__":
    start_client(
        server_address="127.0.0.1:8080",
        client=FlowerClient().to_client(),
    )

ServerApp

  • Wrap your existing strategy with ServerApp instead of starting the server via start_server(). Here’s an example:

from flwr.common import Context
from flwr.server import ServerApp, ServerAppComponents, ServerConfig, start_server
from flwr.server.strategy import FedAvg


# Flower 1.10 and later (recommended)
def server_fn(context: Context):
    strategy = FedAvg()
    config = ServerConfig()
    return ServerAppComponents(config=config, strategy=strategy)


app = ServerApp(server_fn=server_fn)


# # Flower 1.8 - 1.9 (deprecated, no longer supported)
# app = flwr.server.ServerApp(
#     config=config,
#     strategy=strategy,
# )


# Flower 1.7 (deprecated, only for backwards-compatibility)
if __name__ == "__main__":
    start_server(
        server_address="0.0.0.0:8080",
        config=config,
        strategy=strategy,
    )

Deployment

  • In a terminal window, start the SuperLink using flower-superlink. Then, in two additional terminal windows, start two SuperNodes using flower-supernode (2x). There is no need to directly run client.py and server.py as Python scripts.

  • Here’s an example to start the server without HTTPS (insecure mode, only for prototyping):

Tip

For a comprehensive walk-through on how to deploy Flower using Docker, please refer to the Run Flower using Docker guide.

# Start a SuperLink
$ flower-superlink --insecure

# In a new terminal window, start a long-running SuperNode
$ flower-supernode \
     --insecure \
     --superlink 127.0.0.1:9092 \
     --clientappio-api-address 127.0.0.1:9094 \
     <other-args>

# In another terminal window, start another long-running SuperNode (at least 2 SuperNodes are required)
$ flower-supernode \
     --insecure \
     --superlink 127.0.0.1:9092 \
     --clientappio-api-address 127.0.0.1:9095 \
     <other-args>
  • Here’s another example to start both SuperLink and SuperNodes with HTTPS. Use the --ssl-ca-certfile, --ssl-certfile, and --ssl-keyfile command line options to pass paths to (CA certificate, server certificate, and server private key).

# Start a secure SuperLink
$ flower-superlink \
    --ssl-ca-certfile <your-ca-cert-filepath> \
    --ssl-certfile <your-server-cert-filepath> \
    --ssl-keyfile <your-privatekey-filepath>

# In a new terminal window, start a long-running SuperNode
$ flower-supernode \
     --superlink 127.0.0.1:9092 \
     --clientappio-api-address 127.0.0.1:9094 \
     --root-certificates <your-ca-cert-filepath> \
     <other-args>

# In another terminal window, start another long-running SuperNode (at least 2 SuperNodes are required)
$ flower-supernode \
     --superlink 127.0.0.1:9092 \
     --clientappio-api-address 127.0.0.1:9095 \
     --root-certificates <your-ca-cert-filepath> \
     <other-args>

Simulation (CLI)

Wrap your existing client and strategy with ClientApp and ServerApp, respectively. There is no need to use start_simulation() anymore. Here’s an example:

Tip

For a comprehensive guide on how to setup and run Flower simulations please read the How-to Run Simulations guide.

from flwr.client import ClientApp
from flwr.common import Context
from flwr.server import ServerApp, ServerAppComponents, ServerConfig
from flwr.server.strategy import FedAvg
from flwr.simulation import start_simulation


# Regular Flower client implementation
class FlowerClient(NumPyClient):
    # ...
    pass


# Flower 1.10 and later (recommended)
def client_fn(context: Context):
    return FlowerClient().to_client()


app = ClientApp(client_fn=client_fn)


def server_fn(context: Context):
    strategy = FedAvg(...)
    config = ServerConfig(...)
    return ServerAppComponents(strategy=strategy, config=config)


server_app = ServerApp(server_fn=server_fn)


# # Flower 1.8 - 1.9 (deprecated, no longer supported)
# def client_fn(cid: str):
#     return FlowerClient().to_client()
#
#
# client_app = ClientApp(client_fn=client_fn)
#
#
# server_app = ServerApp(
#     config=config,
#     strategy=strategy,
# )


# Flower 1.7 (deprecated, only for backwards-compatibility)
if __name__ == "__main__":
    hist = start_simulation(
        num_clients=10,
        # ...
    )

Depending on your Flower version, you can run your simulation as follows:

  • For Flower 1.11 and later, run flwr run in the terminal. This is the recommended way to start simulations, other ways are deprecated and no longer recommended.

  • DEPRECATED For Flower versions between 1.8 and 1.10, run flower-simulation in the terminal and point to the server_app / client_app object in the code instead of executing the Python script. In the code snippet below, there is an example (assuming the server_app and client_app objects are in a sim.py module).

  • DEPRECATED For Flower versions before 1.8, run the Python script directly.

# Flower 1.11 and later (recommended)
$ flwr run


# # Flower 1.8 - 1.10 (deprecated, no longer supported)
# $ flower-simulation \
#     --server-app=sim:server_app \
#     --client-app=sim:client_app \
#     --num-supernodes=10


# Flower 1.7 (deprecated)
$ python sim.py

Depending on your Flower version, you can also define the default resources as follows:

  • For Flower 1.11 and later, you can edit your pyproject.toml file and then run flwr run in the terminal as shown in the example below.

  • DEPRECATED For Flower versions between 1.8 and 1.10, you can adjust the resources for each ClientApp using the --backend-config command line argument instead of setting the client_resources argument in start_simulation().

  • DEPRECATED For Flower versions before 1.8, you need to run start_simulation() and pass a dictionary of the required resources to the client_resources argument.

# Flower 1.11 and later (recommended)
# [file: pyproject.toml]
[tool.flwr.federations.local-sim-gpu]
options.num-supernodes = 10
options.backend.client-resources.num-cpus = 2
options.backend.client-resources.num-gpus = 0.25

$ flwr run

# # Flower 1.8 - 1.10 (deprecated, no longer supported)
# $ flower-simulation \
#     --client-app=sim:client_app \
#     --server-app=sim:server_app \
#     --num-supernodes=10 \
#     --backend-config='{"client_resources": {"num_cpus": 2, "num_gpus": 0.25}}'
# Flower 1.7 (in `sim.py`, deprecated)
if __name__ == "__main__":
    hist = start_simulation(
        num_clients=10, client_resources={"num_cpus": 2, "num_gpus": 0.25}, ...
    )

Simulation (Notebook)

To run your simulation from within a notebook, please consider the following examples depending on your Flower version:

  • For Flower 1.11 and later, you need to run run_simulation() in your notebook instead of start_simulation().

  • DEPRECATED For Flower versions between 1.8 and 1.10, you need to run run_simulation() in your notebook instead of start_simulation() and configure the resources.

  • DEPRECATED For Flower versions before 1.8, you need to run start_simulation() and pass a dictionary of the required resources to the client_resources argument.

Tip

For a comprehensive guide on how to setup and run Flower simulations please read the How-to Run Simulations guide.

from flwr.client import ClientApp
from flwr.common import Context
from flwr.server import ServerApp
from flwr.simulation import run_simulation, start_simulation


# Flower 1.10 and later (recommended)
# Omitted: client_fn and server_fn

client_app = ClientApp(client_fn=client_fn)

server_app = ServerApp(server_fn=server_fn)

run_simulation(
    server_app=server_app,
    client_app=client_app,
)


# # Flower v1.8 - v1.10 (deprecated, no longer supported)
# NUM_CLIENTS = 10  # Replace by any integer greater than zero
# backend_config = {"client_resources": {"num_cpus": 2, "num_gpus": 0.25}}
#
#
# def client_fn(cid: str):
#     # ...
#     return FlowerClient().to_client()
#
#
# client_app = ClientApp(client_fn=client_fn)
#
# server_app = ServerApp(
#     config=config,
#     strategy=strategy,
# )
#
# run_simulation(
#     server_app=server_app,
#     client_app=client_app,
#     num_supernodes=NUM_CLIENTS,
#     backend_config=backend_config,
# )


# Flower v1.7 (deprecated)
NUM_CLIENTS = 10  # Replace by any integer greater than zero
backend_config = {"client_resources": {"num_cpus": 2, "num_gpus": 0.25}}
start_simulation(
    client_fn=client_fn,
    num_clients=NUM_CLIENTS,
    config=config,
    strategy=strategy,
    client_resources=backend_config["client_resources"],
)

Further help

Most official Flower code examples are already updated to Flower 1.13 so they can serve as a reference for using the Flower 1.13 API. If there are further questions, join the Flower Slack (and use the channel #questions) or post them on Flower Discuss where you can find the community posting and answering questions.

Important

As we continuously enhance Flower at a rapid pace, we’ll be periodically updating this guide. Please feel free to share any feedback with us!

Happy migrating! 🚀