Why Dropbox rebuilt its monolith as a managed platform
Dropbox’s product must stay reliable and responsive for more than 700 million registered users, generating at least 300,000 requests per second. But the internal systems that supported that scale had become a bottleneck. The company's central Python monolith, Metaserver, had grown into a tangled, hard-to-maintain codebase that slowed development and made deployments risky. In response, Dropbox built Atlas—a managed platform that delivers most of the benefits of a Service Oriented Architecture (SOA) without the operational overhead of running many independent services.
Metaserver: the monolith that outgrew its model
Most server-side development at Dropbox happens in a shared server monorepo, with over 3 million lines of Python code in the monolithic Metaserver application. About half of all commits to the server repository touch Metaserver, which was created in 2007 and has served as the core of Dropbox's product functionality. While platform-level components like authentication, metadata storage, filesystem, and sync were split into separate services, Metaserver remained responsible for the majority of product routes.
Extremely simplified view of existing serving stack
The codebase was originally organized in a simple library/model/controllers pattern with no central curation. Over time, that structure eroded. Teams introduced import cycles to unblock features rather than refactor, and because many teams shared the codebase, no single team felt ownership over its quality. The result was one of the most disorganized and tangled codebases in the company.
//metaserver/controllers/ …
//metaserver/model/ …
//metaserver/lib/ …
Deployments became unpredictable
Metaserver is pushed to production daily for all users. But with hundreds of developers contributing to the same codebase, the chance of at least one critical bug landing each day was high. When that happened, the whole monolith had to be rolled back or cherry-picked, which made push cadence unreliable for everyone. Developers couldn't plan around a clean deployment schedule, because another team's unrelated code could force a full-cluster rollback on any given day.
Infrastructure stagnated
A monolith of millions of lines of code made infrastructure upgrades nearly impossible to stage safely. Metaserver ran on a legacy Python framework that was no longer used elsewhere at Dropbox. The framework only supported HTTP/1.0, while modern libraries expect HTTP/1.1 as a minimum. Internal monitoring and tracing tools—already integrated into newer infrastructure—had to be re-implemented for Metaserver's different framework stack. This technical debt meant that even small platform improvements were impractical or simply never happened.
Why independent services weren’t the answer
Dropbox launched an SOA initiative to address these problems. The plan had two phases: first, make it easy to build services outside Metaserver by extracting core functionality like identity management and exposing it via RPC; second, break Metaserver into smaller services owned by individual teams. After more than a year and a half of work on the first milestone, the team learned something important: as more services were added to the critical path for customer traffic, maintaining high reliability became harder, and the problem would only grow as product teams were asked to run their own services.
That led to a reassessment. Product functionality at Dropbox falls into two categories:
- Large, complex systems—like the logic around sharing a file—involving stateful behavior, access control, rate limits, and quotas. These need dedicated teams that can manage the operational burden.
- Small, self-contained functionality—like a homepage—which is simple and rarely changes, with limited failure modes.
The key insight was that small functionality doesn't need an independently operated service. Planning capacity, setting up alerts, and managing multihoming for a simple endpoint is unnecessary overhead. Teams mostly want to write logic, have it run when users hit a route, and receive basic alerts when errors spike. Most of Dropbox's product functionality falls into this category, and most operational issues share common themes: unexpected traffic spikes or outages in underlying services.
Atlas: a hybrid model
Atlas is Dropbox's response to these learnings. It provides the user experience of a serverless system—similar to AWS Fargate—for product developers, while being backed by automatically provisioned services behind the scenes. The goal is to give developers the benefits of SOA—clear abstractions and separation of concerns—without requiring them to operate a service.
Developers writing code in Atlas only need to define the interface and implementation of their endpoints. Atlas handles creating a production cluster to serve those endpoints, and the Atlas team is responsible for pushing code to and monitoring those clusters. This shifts the developer experience from dealing with the monolith's shared-code pitfalls to a cleaner, more isolated model.
Before and after Atlas
Large components, such as sharing or sync services, continue to run as independent services with their own teams and push schedules. Atlas is designed to coexist with these systems, not replace them. The platform targets the majority of product functionality that is self-contained, which is where the operational cost of a full SOA model outweighs its benefits.
By combining the convenience of a monolith with the isolation of services, Atlas gives Dropbox a path forward that doesn't rely on either extreme. The monolith's problems—tangled code, unpredictable deployments, and infrastructure debt—are addressed for the common case, while teams that operate large, complex systems retain the control they need.
What we set out to fix
The Metaserver monolith had a few structural weaknesses we wanted to correct. Code had no real abstractions for sharing, which led to heavy coupling between features; a bug in one feature could stall pushes for everyone. Operational overhead was also high, and because all routes were served from one large cluster, a heavy or failing feature could drag down unrelated ones like the homepage.
We defined five targets for the replacement platform:
- Introduce code structure that reduces coupling and makes new code easier to read and modify.
- Enable independent, consistent pushes so teams are not blocked by bugs in unrelated code, and lay groundwork for teams to push their own code.
- Minimize operational busywork by keeping monolith-style automation: automatic capacity management, alerts, canary analysis, and pushes.
- Unify serving on standard open source components such as gRPC.
- Isolate critical features like the homepage so an overload or bug in one feature does not spill over to others.
We considered off-the-shelf platforms, but to keep migration risk and engineering cost low, we chose to host services on the same deployment orchestration platform already used by the rest of Dropbox. Custom components such as the Bandaid request proxy were dropped in favor of open source systems like Envoy that fit our requirements.
Technical design overview
The project centered on three workstreams.
- Componentization: De-tangle the codebase by feature into components with a single owner each, enforce that new functionality cannot be added by non-owners, and favor code sharing over RPC instead of shared libraries.
- Orchestration: Automatically turn each component into a service on the deployment platform with less than 50 lines of boilerplate, route requests through Envoy to the correct service based on the request path, and switch inter-service communication from HTTP to gRPC.
- Operationalization: Automatically configure a daily deployment pipeline per component, with alerts, regression analysis, and automatic pause/rollback on failures, plus autoscaling for each component based on traffic.
Breaking up the code with Atlasservlets
Atlas introduces Atlasservlets (pronounced “atlas servlets”) as a logical, atomic grouping of routes. The home Atlasservlet, for example, holds all routes used to build the Dropbox homepage; the nav Atlasservlet contains routes for the navigation bar. Product teams mapped every route in Metaserver to an Atlasservlet before the migration, producing more than 200 Atlasservlets across 5,000-plus routes.
//atlas/home/ …
//atlas/nav/ …
//atlas/<some other atlasservlet>/ …
Each Atlasservlet owns a private directory in the codebase. The owner controls all content in that directory and no one else may import from it. This structure inherently forces every endpoint to live in a private directory and makes cross-component code sharing an explicit decision rather than the default outcome of contributing to the monolith.
Because the Atlasservlet is part of the directory path, we could generate production configuration automatically. Server-side code builds with Bazel, and we prevented unwanted imports using Bazel's visibility rules, which let library owners control which code can depend on their libraries.
Removing import cycles
Breaking the codebase required eliminating most Python import cycles. That took several years of scripting and manual refactoring, and we kept cycles from returning by enforcing the same Bazel visibility rules.
Orchestration: every servlet its own cluster
Atlas cluster strategy
Each Atlasservlet operates as its own cluster. That yields three benefits:
- Isolation by default: A misbehaving route only affects other routes in the same Atlasservlet, which the same team owns anyway.
- Independent pushes: Each servlet can be deployed separately, letting product developers control the timing and consistency of their own releases.
- Consistency: Each Atlasservlet behaves like any other internal Dropbox service, so global infrastructure tooling, such as performance profiling, works across all servlets.
We standardized on gRPC throughout. Since the platform still needs to serve HTTP traffic, Envoy handles gRPC-HTTP transcoding out of the box. The migration to gRPC was largely automated with an adapter that converts an existing endpoint into the interface gRPC expects, including restoring any in-memory state the endpoint relies on. That approach also kept endpoints compatible with both Metaserver and Atlas during the transition, so traffic could be moved safely between the two implementations.
Managing the fleet
The managed-experience payoff is that developers write features without worrying about production operations, while retaining isolation and other standalone-service benefits. The cost is that one team is operationally responsible for all 200+ clusters. We built tooling to make that workload feasible.
Because Atlas is stateless, code changes are the primary source of failure. Tight push guardrails therefore eliminate most risk. Each Atlas service has three deployments: canary, control, and production. Only a small random percentage of traffic goes to canary and control. During a push, canary gets the new code while control restarts with the old code at the same time, so both start from the same baseline.
Canary analysis
A canary analysis service, similar to Netflix's Kayenta, compares metrics such as CPU utilization and route availability between canary and control. If canary performs equal to or better than control, the push moves forward. If it regresses, the push stops automatically and owners are notified. Alerts run throughout the pipeline, including between canary, control, and production steps, so a problem can pause and roll back a cluster's deployment on its own.
Mistakes still slip through. When that happens, Atlas's default isolation contains the damage: broken code only impacts its one cluster and can be rolled back independently without blocking pushes elsewhere.
Autoscaling instead of manual capacity planning
Large numbers of small clusters make each more sensitive to traffic surges than a shared monolith would be. A 10x jump in route traffic is easy to absorb in a big shared pool but hard for a small dedicated cluster. Manually planning capacity for 200+ clusters would be crippling, so we built an autoscaler.
The autoscaler watches each cluster's utilization in real time and provisions machines to keep at least 40% free capacity per cluster, removing the need for manual capacity planning. It reads metrics from Envoy's Load Reporting Service and uses request queue length to determine cluster size.
Rolling Out in Increments
Given the size of the legacy codebase, the team deliberately avoided a risky big-bang replacement. Instead, the execution plan relied on "stepping stones": every phase of the project was designed to deliver standalone value, so that a failure in any subsequent phase would not render earlier work useless.
- Testing frameworks in Metaserver were sped up first, anticipating that an Atlas-based serving stack in tests could introduce regressions in test times.
- A hard requirement was set to significantly improve memory efficiency and reduce out-of-memory (OOM) kills when moving from Metaserver to Atlas. The team delivered these efficiency gains to Metaserver directly, rather than coupling them to the migration.
- A load test was built to prove that an Atlas MVP could handle Metaserver traffic. That same load test was later reused to validate Metaserver's performance on new hardware for an unrelated project.
- Workflow simplifications were backported to Metaserver wherever feasible, including improvements to its web workflows.
- Metaserver development workflows fall into three protocol categories: web, API, and internal gRPC. Atlas focused on internal gRPC first, which let the team de-risk the new serving stack without tackling riskier pieces like gRPC-HTTP transcoding. This also created an opportunity to improve internal gRPC workflows independently of the rest of the migration.
Obstacles Along the Way
A migration of this scale inevitably surfaced several significant challenges:
- The legacy HTTP serving stack contained quirky, surprising behavior that was hard to replicate. Porting it without regressions required reading original source code, reusing legacy library functions, and leaning on integration tests. The team also designed a set of tests that compared byte-by-byte outputs between the old and new systems.
- Splitting Metaserver into 200+ Python processes was a win in production but infeasible in the integration testing framework. The team merged the processes back into a monolith for local development and test purposes, with heavy Bazel rule integration so developers could reference Atlas servlets as regular services.
- Some production assumptions were non-obvious and broke during the split. For instance, certain infrastructure services had hardcoded the identity of Metaserver for access control. Mitigating this required a meticulous, incremental rollout plan with clear visibility into risks at each stage, plus careful monitoring of metrics throughout.
- Engineer workflows had grown organically with the monolith, and getting simple work done required a large amount of context. To make sure Atlas addressed real pain points, key product developers were brought into the design process, and multiple rounds of iteration shaped a roadmap covering both product and infrastructure needs.
Current Status and Lessons
Atlas is now serving more than 25% of previous Metaserver traffic, and the remaining migration has been validated in tests. The team sees a clear path to deprecating Metaserver in the near future.
The most important takeaway from this multi-year effort is that code composition matters early. Without it, technical debt and complexity compound quickly. Dismantling import cycles and decomposing Metaserver into feature-based directories was the most strategically effective part of the project; it kept new code from adding to the problem and made the codebase easier to understand.
The project reinforced that monoliths have real benefits, and that blindly splitting one into services would have increased operational load. Developers don't care about the distinction between monoliths and services; they want the lowest-overhead path to shipping value to customers. A managed platform that removes operational busywork like capacity planning while preserving flexibility like fast releases is the way forward.



