How to start Drupal hosting sizing before you pick servers
A team with a launch date needs to know how many web, database and search servers to budget before the platform is built, and the honest starting answer is that server counts come from four inputs: the traffic mix, the cache hit rate, PHP memory per request, and database and search load. Those inputs are then checked by load testing against the real site before launch. Start by writing down what the site must serve at launch and at peak, split into anonymous visitors, who can usually be served from cache, and logged-in editors and customers, who usually cannot. That split is the single most important number in the exercise, because it decides how much of the traffic ever reaches PHP and the database.
This page explains that method and the decisions behind it. It does not give a server count, instance size or monthly cost for your site, because none of those can be known from a generic page. Drupal runs on Apache or Nginx, with MariaDB, MySQL, PostgreSQL or SQLite as the database, according to the Drupal system requirements. The method is the same for each combination, but the figures are not. A PHP-FPM pool, a MariaDB buffer setting and a Solr heap size all behave differently on each stack, so every number you budget must be measured in your own environment, with your own modules, themes and content volumes. Confirm the exact stack, the Drupal major version and the PHP version with your platform team first, since the PHP version limits which Drupal releases you can run.
Which sizing inputs change the server count and when
This table maps each sizing input to the tier it drives and the point in a project when it matters most. Read it before estimating servers, so that traffic, caching and data assumptions are settled before anyone picks instance sizes. The main takeaway is that anonymous traffic and cache hit rate usually decide the web tier, while authenticated traffic and search volume decide the rest.
| Input | What it drives | When it matters most |
|---|---|---|
| Anonymous page traffic | Web tier and PHP workers when pages are not served from cache | Before launch, and again after any campaign or traffic spike that bypasses the cache |
| Authenticated editor and member traffic | PHP workers, database connections and cache misses, since personalized pages are usually uncached | When members, editors or logged-in customers form a large share of sessions |
| Cache hit rate | Web tier, PHP workers and database load, because every miss runs the full Drupal bootstrap | From the first load test onward, since a small change in hit rate can move load a lot |
| PHP memory per request | PHP-FPM pool size and the memory available per worker on each web node | When adding views, large entity loads, image processing or heavy contributed modules |
| Database size and growth | Database server storage, buffer memory and backup window | During data modeling and before migration, and whenever content volume is expected to grow quickly |
| Search index size and query volume | Search server nodes, memory and disk, plus query capacity on the search tier | When Search API with Solr or another external engine is planned, and when faceted search is used heavily |
| Cron and queue work | Worker processes, scheduled job timing and load on the database during batch runs | When imports, syndication or large queues run alongside peak traffic |
| File and image storage | Shared file storage, object storage requests and image derivative generation on the web tier | When editors upload large media volumes or image styles are generated on demand |
| Platform versions | Constrains the PHP and database versions, which then limit the instance and database options | Before choosing the operating system, PHP build and database engine, because the Drupal requirements pages set the supported versions |
Why anonymous and authenticated traffic size the platform differently
Anonymous visitors and authenticated users place very different loads on a Drupal platform, so the first sizing decision is how much of the traffic can be answered from cache. Drupal's page cache and the reverse proxy or CDN in front of it can serve anonymous page views without running most of the application, because every anonymous visitor receives the same cached response for a given URL. A content-heavy public site with mostly anonymous reads therefore needs far less PHP and database capacity per visit than its raw traffic figure suggests. The web tier, cache layer and edge network carry most of that load, and the origin is sized mainly for cache misses, cron, search indexing and editorial work.
Logged-in editors, members and customers are different. Each session carries its own identity, permissions and often its own content, so most of their requests bypass the shared cache and go to PHP and the database. Editorial sessions are especially expensive because they load forms, media libraries and administrative views that query heavily. To estimate the split, combine your analytics platform's data on anonymous and signed-in sessions with the expected role mix from your requirements: how many staff will edit, how many members will log in, and what each group does on each visit. Ask the analytics owner to confirm how sessions are counted, since a single signed-in user can generate many requests.
Some features quietly convert cacheable pages into uncached ones. A personalized block, a commerce cart in the header, a 'hello, name' greeting or a session-dependent view makes the response vary by user, and if caching is not configured for those cache contexts, the whole page may fall back to the origin. Check every template and block for dependencies on the user, the session, the cart or the request, and confirm with your CDN or reverse proxy vendor which headers and cookies they honor. Treat each such feature as a sizing input, and plan a load test that includes the cookie-carrying traffic, not only anonymous browsing.
Cache hit rate, PHP memory, database size and search as the main drivers
Drupal capacity rarely comes down to one number. Traffic mix, cache effectiveness, PHP workers, database growth, search indexing and background jobs interact, so a bottleneck in one usually shows up as a symptom in another. Each card below names one driver, what pushes it up or down, and what the sizing team should measure before launch.
Cache hit rate and backend choice
Cache hit rate decides how many requests reach PHP and the database at all. Anonymous pages are the easiest to cache, while authenticated users, personalized blocks, frequent content edits and short max-age values lower the hit rate and multiply the work per visitor. The cache backend also matters: the default database-backed cache competes with application queries, so the team should record which backend is in use and measure hit and miss rates under a realistic traffic mix.
PHP memory and worker count
Each request consumes PHP memory and occupies a worker for its full duration, so concurrency is limited by workers, not by page count. Heavy modules, large entity loads, image processing and complex views raise memory use per request, while a high cache hit rate lowers it. Measure peak memory per request type, time spent per request, and how many workers saturate the host at the target concurrency, then confirm the PHP version against the current Drupal requirements.
Database size and query load
Database size grows with content revisions, watchdog or log entries, cache tables, users and configuration, and it sets backup and restore time as much as query speed. Query load rises with uncached authenticated traffic, complex views and contributed modules that issue many small queries. Check the database engine and version against your Drupal release, track table growth over a sample period, and measure slow queries and connection counts during the load test.
Search server load and index size
When search runs on a dedicated engine, index size and query volume set a separate capacity budget. Index size grows with indexed entities, languages and fields, and each content edit or bulk import triggers index updates that compete with reads. Ask the team to confirm how the search module indexes content (on save or through cron), measure index size per content type, and test query latency while indexing runs at the same time as peak front-end traffic.
Cron and queue throughput
Cron, queue workers and scheduled jobs run outside page requests, but they consume the same database, memory and file storage. Long cron runs can overlap with the next run, and queues that fall behind delay content publishing, search indexing, image derivatives and feeds. Measure how long cron takes, how many queue items are created per hour at peak editing activity, and whether workers run on the web nodes or on separate hosts that the sizing model accounts for.
How sizing differs for a content site, a logged-in portal and a multisite estate
A mostly anonymous content site is sized mainly by how often anonymous requests can be answered from cache. Most visitors receive the same rendered page, so the cache hit rate at the reverse proxy, CDN and Drupal's page cache determines how many requests reach PHP and the database. Traffic spikes from campaigns or news events are the usual test, and the cache layer, not the application servers, is often where the first constraint appears.
- Cache hit rate: Measure the share of anonymous page requests served from the CDN or reverse proxy and from Drupal's internal page cache, by route and by time of day.
- Cache invalidation volume: Editors who publish often invalidate many cache tags at once. Measure how many purges a typical publishing hour produces and how long they take to clear.
- Cron and queue timing: Scheduled imports, search indexing and feed fetches can run during peak traffic. Confirm when cron runs and whether it competes with web requests.
- Database read load: Anonymous traffic should reach the database rarely. If it does, check for uncached blocks, views with no cache tags or pages that vary by a cookie.
- Search and media: Search servers and image derivative generation are separate costs. Ask for the expected query volume and the image styles in use.
Before launch, confirm with your team which pages are expected to be cached, and load test a cold cache and a warm cache separately, since the results differ widely.
A logged-in portal is sized around per-user work. Authenticated requests bypass most anonymous page caching, so each session can cost PHP time and database queries for its own dashboard, permissions and personalized blocks. Concurrency, meaning the number of users active at the same moment, matters more than total monthly users, and the session store and PHP worker pool become primary constraints.
- Concurrent sessions: Measure peak simultaneous logged-in users during business hours, not daily registered users, and ask whether sessions are long-lived.
- PHP worker capacity: Check how many PHP-FPM workers each application node can run and how long a typical authenticated request holds one.
- Permission and view complexity: Views with access checks, role-based blocks and per-user content prevent effective caching. Measure query counts per authenticated page.
- Session and login load: Confirm where sessions are stored, and whether single sign-on callbacks or login bursts happen at the start of a shift.
- Cache backend: Confirm whether Redis or Memcache is in use for the cache bins that hold per-user data, and what memory it needs.
Before launch, run a load test with scripted logged-in users that follow realistic paths, and confirm the expected concurrency figure with the business owner before testing.
A multisite estate serves many sites from one codebase, so sizing is a shared-resource problem. Each site may have its own database and files, but they share PHP pools, cron runs, deployments and sometimes the same database server. Traffic from one busy site can degrade all the others, and a single failed update can affect every site in the estate at once.
- Shared database server: Measure combined connections, query rate and storage across all site databases, not each database alone. Confirm the maximum connection setting.
- Shared PHP pool: Sites that share a PHP-FPM pool compete for workers. Ask whether pools are separated per site or per group, and measure worker wait times during the busiest site's peak.
- Shared cron: Running cron for many sites in sequence can delay all of them. Confirm whether each site has its own schedule and how long a full cron cycle takes.
- Shared files and storage: Confirm whether file storage is per site, and how much capacity each site adds over time.
- Release coupling: A single codebase means one release train. Confirm the rollback path if one site fails after an update.
Before launch, load test the busiest site while the others are idle and again with all sites active, because the shared constraints appear only in the combined run.
How to run a sizing exercise and prove it with load tests before launch
This sequence turns traffic guesses into a tested server plan by measuring how the Drupal site behaves under agreed peak load, with headroom and recovery proven before go-live.
Collect traffic and content forecasts
The business owner and the product team supply expected visits, peak periods, logged-in staff counts, content volume and growth over the planning horizon, with the source of each number recorded. The platform team checks the forecast against current analytics if the site already exists, and flags any figure that is an estimate rather than a measurement. Done means a written traffic model with a named peak scenario that everyone has signed off, including which numbers are assumptions still to be validated.
Define user roles and critical journeys
Product owners and the Drupal lead list the anonymous journeys (search, landing pages, article views, checkout if any) and the authenticated journeys (editor saves, moderation, logged-in portals, form submissions) that matter most to the business. Each journey gets a share of expected traffic and a required response expectation. Done means a journey inventory that maps every test script to a real user group and to a business reason for it being tested.
Set the caching design before sizing
The platform team documents which pages are served from the page cache, which from the dynamic page cache, and which bypass caching entirely, such as authenticated sessions and forms. They confirm the expected cache hit rate for anonymous traffic, the cache backend (database, Redis or Memcache), and the reverse proxy or CDN rules, because these choices change server counts more than any other setting. Done means a caching diagram and a stated hit-rate assumption that the load test will verify, not assume.
Build a baseline environment matching production
The platform team builds staging with the same web server, PHP and database versions, the same Drupal core and contributed module versions, the same cache backend, and comparable server sizes and file storage. They check the PHP version, database engine and version, and OPcache settings against production with a side-by-side comparison, and they confirm that cron and queue workers run the same way. Done means a documented configuration diff that is empty for every item affecting performance, because a test on different versions proves little about production.
Run load tests on anonymous and authenticated journeys
The performance engineer runs scripted tests for each journey at the agreed peak, starting from a warm cache and then from a cold cache, with realistic think times and a data set that approximates production content volume. Separate runs cover anonymous traffic and authenticated traffic, since the second bypasses most caching. Done means results showing response times, error rates and throughput at the target peak, plus the load level at which response times begin to degrade.
Measure PHP memory, database and search behavior
During each run, the platform team records PHP-FPM worker utilization and memory per request, database connections, slow queries, lock waits and disk I/O, and search server latency and indexing time if Search API or another search service is in use. They compare these figures with the configured limits to find which resource saturates first. Done means a bottleneck report that names the first limit reached and the evidence for it, so that fixes target the real constraint.
Test recovery after cache flush and deployment
The performance engineer runs a full cache flush under load and a simulated deployment with database updates and configuration import, measuring how long response times stay elevated and whether errors occur during rebuild. They confirm the site stays within agreed response behavior while caches warm. Done means a recorded recovery curve and a documented rule for warming caches or staging deployments, which the operations team will follow in production.
Fix bottlenecks and repeat the tests
The platform and development teams address the bottlenecks identified, such as caching rules, query changes, PHP-FPM pool settings or search index tuning, and then rerun the same scripts under identical conditions. The sizing model is updated with the new measurements and the headroom above the agreed peak is calculated. Done means a final report showing target response behavior at peak, the remaining headroom, the recovery result, and the list of assumptions that production monitoring must confirm after launch.
Security and operational risks that load tests can expose
Load tests put a site under conditions it will not see on a quiet day, and several of the risks they expose are security or operational rather than purely performance. The cards below cover the test data, exposed paths, cache behavior, bot traffic, patching and monitoring decisions that should be settled before the first test run, not discovered during launch.
Test data in production
Load tests that run against a copy of production data, or against production itself, can expose real personal data to test tools, logs and shared test accounts. Drupal sites often hold user profiles, form submissions and order records, and a load test that replays them spreads that data further than the original system allowed. Ask who can read test output and where scripts store credentials. Use synthetic content sized to match production row counts so the database still behaves realistically.
Admin and login paths under test
Load scripts often hit /user/login, /admin and other authenticated routes because those paths are expensive to render. If these paths are reachable from the test network or are not rate limited, the test can lock accounts, flood the mail system with password reset messages or trigger alerts that the security team cannot distinguish from an attack. Confirm which paths the script may call, use dedicated test accounts with known lockout behavior, and inform the security operations team before each run.
Cache poisoning and personalized content
A reverse proxy or CDN that caches a page rendered for one authenticated user can serve that content to anonymous visitors if cache variation is misconfigured. Load tests that mix anonymous and logged-in sessions are a good way to find this, because the test will show whether personalized responses carry cache headers they should not. Check that authenticated responses are marked uncacheable, that cookies vary the cache correctly, and that the test verifies response bodies, not only status codes.
Bot traffic that mimics load
Rate limiting and bot defenses may behave differently under a load test than they will under real traffic, and crawlers can add requests that look like test load in the same graphs. Without agreed source addresses and a test header, the team cannot separate its own traffic from scraping or credential stuffing during the test window. Agree on a tagged traffic source, record its addresses, and decide in advance whether the test should be allowed to trigger blocks.
Patch windows that change capacity
Drupal security releases come on the third Wednesday of the month, so a capacity plan should assume that a patch may need to be applied at short notice. A patch can change PHP, database or module code paths, and the performance measured before the patch may not hold after it. Plan a repeat of the critical load scenarios after material updates, keep a rebuild path for container images or artifacts, and allow time in the operations calendar for emergency patching.
Monitoring absent during launch
Launch is when traffic is least predictable and when teams are least able to read the signals they have. If application logs, database slow query logging, PHP-FPM status and cache hit metrics are not collected before the test, the team will have load results without the evidence needed to explain them. Confirm that dashboards, alert thresholds and on-call rotations exist and have been exercised during the load test, and that someone owns the decision to roll back.
What buyers ask about Drupal hosting sizing and capacity planning
What does Drupal sizing work cost and what drives the price?
The price follows from the written scope, not from a standard rate. The main cost drivers are the number of environments to model, the number of user journeys to test, whether anonymous and logged-in traffic are both in scope, the test tooling needed, and whether the client already has monitoring that provides baseline data. Sizing is usually a one-time piece of work, while the servers it recommends carry recurring hosting costs. Ask any partner for a quote that lists each deliverable, the environments covered, the test scenarios, the tools used and which of those tools the client keeps afterward. DeployOpen does not publish a price list, and a quote follows from the agreed scope.
How long does capacity planning take, and what sets the schedule?
The schedule depends on how quickly reliable traffic data and a production-like environment become available, more than on the sizing calculation itself. Key inputs are the number of journeys to script, how many test cycles are needed to find the bottleneck, and whether the team must fix caching or database issues before the final run. Ask for a timeline in the written scope that separates data collection, test build, test runs and reporting, and that names the dependencies the client must clear, such as access approvals and environment provisioning.
What must our team provide before sizing can start?
Plan to supply four inputs: historical traffic data from your web server, CDN or analytics tools, including peak periods and the split between anonymous and authenticated visits; a content forecast covering node counts, media volume, expected growth and bulk imports; the role and permission model, since editors, moderators and members load pages differently; and access to a staging environment that resembles production in PHP version, database engine, cache backend, search service and network path. Without the last item, load test results describe a different system. Confirm each input in the written scope.
Can our internal team size the platform alone, and what skills does it need?
An internal team can do it if it has people who understand Drupal caching behavior, PHP-FPM and database tuning, load testing tools and the application's critical paths. The work needs someone who can read cache hit ratios and database slow query logs, someone who can script realistic user journeys including logged-in sessions, and someone who can model growth without relying on guesses. Most gaps appear in cache configuration and in tests that ignore authenticated traffic. Weigh these skills against the time your team has, and consider a partner for a one-time sizing pass while your own staff keeps ownership of the results.
Does a fixed-scope engagement suit capacity planning?
Yes, when the questions are defined in advance. A fixed-scope engagement works well for a defined set of deliverables, such as a traffic model, a sizing recommendation per environment, a test plan, scripted journeys and a report of pass or fail criteria. It is less suited to open-ended tuning after launch, which is better handled through a retainer. DeployOpen works mostly on fixed-scope engagements, with scope, deliverables and timelines agreed in writing before work starts, and offers ongoing support as a retainer where needed.
What should we do if our Drupal site is already live and slow?
Start by measuring before changing anything. Check whether slow responses come from uncached anonymous pages, slow authenticated sessions, long database queries, PHP memory exhaustion, cron jobs competing with web requests or search queries sent to the database. Review your logs and monitoring for the slow periods, then reproduce the pattern in a copy of production if possible. Changes such as enabling a persistent cache backend, adding a reverse proxy or moving search to a dedicated service each affect different bottlenecks, so test them one at a time and keep a rollback path.
Why is a load test not the same as a security test?
A load test measures how the platform behaves under expected and peak demand: response times, error rates, throughput and the point where resources saturate. A security test looks for ways an attacker could abuse the application, its APIs, authentication flows or infrastructure. A site can pass a load test and still have injection flaws, weak access control or exposed admin paths, and a secure site can still fall over under traffic. Each test needs its own scope, tools and acceptance criteria. Drupal security advisories from the Drupal Security Team are a separate input to patching discipline, not a substitute for performance testing.
Limits to confirm with your hosting provider, Drupal version and modules
Capacity numbers are only as good as the platform limits underneath them. The cards below list the constraints to pin down first: the Drupal version and PHP runtime, the database engine, the web server, managed service limits, cache and CDN features, and contributed module behavior. Each one changes the server count or the test plan.
Drupal version and PHP runtime
The Drupal version sets the PHP floor for every server in the estate. Drupal 10 requires PHP 8.1 or later, Drupal 11 requires PHP 8.3 or later and Drupal 12 requires PHP 8.5 or later, and no supported Drupal version runs on PHP 7. Confirm which PHP versions your provider's images or managed runtimes offer, because a platform that lags behind can force a runtime upgrade before the sizing work is valid.
Supported database engines
Drupal runs on MariaDB, MySQL, PostgreSQL or SQLite. The engine and its version determine how connection limits, query plans and replication behave under load, so your benchmark must run on the same engine and version you will operate. Confirm the exact database version your provider supports against the Drupal system requirements before you set connection pool sizes.
Chosen web server
Drupal runs on Apache, Nginx and other web servers. The choice changes how PHP workers are pooled, how static assets and private files are served, and how many requests a node can hold before queuing. Record the server and its worker model in the sizing sheet, and confirm with your team that the rewrite and header rules needed by Drupal are supported on that platform.
Managed database limits
Managed database services impose limits on connections, IOPS, storage growth, backup retention and replica count, and these limits differ by tier and region. Ask the provider for the connection ceiling, the storage performance tied to each size, and the failover behavior. Confirm vendor-specific limits, storage IOPS and network throughput with the provider in writing, because these figures are not fixed across offerings.
CDN and cache backend capabilities
Cache tag purging, edge caching of authenticated responses and cache backend support such as Redis or Memcache depend on the CDN and hosting product you choose. Some products cannot purge by cache tag or cannot vary cache by cookie. Confirm each capability with the vendor before modeling cache hit rates, since the hit-rate assumptions drive how many origin servers you need.
Contributed module resource use
Contributed modules such as search integrations, migration helpers and identity connectors can add queries, cron work or memory use that core testing does not reveal. Where a module's resource profile is unknown, measure it in staging rather than estimating it. Review its maintenance status and the Drupal version it declares support for, because an unmaintained module may hold back the Drupal upgrade path.
How DeployOpen scopes, tests and hands over Drupal capacity plans
DeployOpen plans, implements, hardens, documents and supports self-hosted open-source platforms inside your own infrastructure, including cloud accounts, data centers and air-gapped networks. For Drupal hosting sizing, that means turning traffic assumptions, cache behavior, database and search needs into a written capacity plan that your team can test, run and revisit as the site grows.
How we scope
- Agree the written scope covering traffic profiles, hosting targets, cache layers and load test goals before work starts.
- Collect anonymous and authenticated traffic estimates, content volumes and editorial peaks from your team and analytics data.
- Confirm which Drupal version, PHP version and database engine the capacity plan must support, since these set the baseline.
How we build
- Model web, PHP, database, cache and search tiers separately, showing how each one scales and where it becomes the bottleneck.
- Set up a repeatable load test with realistic anonymous and logged-in user journeys, cron and queue jobs running alongside.
- Record resource limits, PHP memory settings and connection pool sizes in configuration your team can review and version.
How we hand over
- Deliver the capacity model, load test scripts, results and the thresholds that should trigger scaling or further tuning.
- Document the assumptions behind each sizing decision so your team can recheck them when traffic or features change.
- Walk through the runbook for monitoring signals, alert thresholds and the steps to follow when a limit is approached.
How we support
- Offer ongoing support, upgrades and continuous testing as a retainer once the initial capacity plan is in production.
- Re-run load tests before major releases, Drupal core upgrades or significant new features that change the traffic pattern.
- Confirm with your team which Drupal release and security schedule applies, since capacity changes can follow version changes.

