Flywheel, MagicSync, and Blueprints: Mastering the Agency WordPress Workflow
Running a WordPress agency means juggling dozens of client sites, each with its own theme, plugins, configurations, and hosting quirks. The operational overhead of spinning up new sites, deploying changes safely, and managing billing across accounts can eat hours out of every week. Flywheel was built specifically for this problem. Before WP Engine acquired the company in 2019, Flywheel had already carved out a strong niche among agencies and freelancers who needed fast local development, painless deployments, and client-friendly billing transfers. Those features remain intact today, and when you combine them with Local (formerly Local by Flywheel), MagicSync, Blueprints, and Git-based deployment pipelines, you get a workflow that dramatically cuts the time between “new client signed” and “site is live.”
This guide walks through the full agency workflow on Flywheel: from local development with Local, through MagicSync differential deployments, Blueprint templating, Git-based CI/CD via SSH, caching and CDN behavior, staging environments, team collaboration, client billing transfers, and the practical differences between Flywheel and WP Engine post-acquisition. Every section includes real commands, configuration files, and the kind of operational detail that documentation pages tend to skip.
Local to Flywheel: The One-Click Push/Pull Workflow
Local is a free desktop application that runs full WordPress environments on your machine using Docker or a custom Lightning service. Each site gets its own PHP version, web server (Nginx or Apache), and MySQL or MariaDB instance. The real power for agency work comes from the direct integration between Local and Flywheel hosting. When you connect your Flywheel account inside Local, every site on your Flywheel account appears in a sidebar list, and you can push or pull with a single click.
Setting Up the Connection
Open Local, navigate to Preferences, and sign in with your Flywheel account credentials. Once authenticated, the “Connect” tab under any local site will show a dropdown of your Flywheel-hosted sites. Select the target site and Local stores that pairing. From that point forward, pushing and pulling is a matter of clicking “Push to Flywheel” or “Pull from Flywheel” and choosing which components to sync.
The push/pull dialog gives you three toggles: Files, Database, and Find & Replace. Files covers everything in wp-content (themes, plugins, uploads). Database pushes or pulls the full MySQL dump. Find & Replace automatically rewrites URLs so that your local domain (something like mysite.local) gets swapped for the production domain, and vice versa. This last toggle is critical. Without it, you end up with serialized data containing the wrong domain, which breaks widgets, Customizer settings, and any plugin that stores full URLs in the database.
What Actually Happens During a Push
When you click “Push to Flywheel,” Local does the following under the hood:
- Exports the local database to a SQL dump file.
- Runs a search-replace on the dump, swapping the local domain for the remote domain. This uses a serialization-aware replacement algorithm, so serialized PHP strings in option values and post meta get their byte counts updated correctly.
- Compresses the wp-content directory (excluding node_modules, .git, and other ignored paths).
- Uploads both the SQL dump and the compressed archive to Flywheel via their API over HTTPS.
- Flywheel unpacks the archive into the site’s wp-content directory on the server and imports the SQL dump into the live database.
The pull operation is the reverse: Flywheel packages up the remote wp-content and database, sends them to Local, and Local imports them with the domain swap going in the opposite direction.
There are a few things to watch out for. The push replaces the entire wp-content directory and the entire database. It is not a merge. If a client has uploaded new media files to the live site while you were working locally, pushing your local files will overwrite those uploads. The safe workflow is to always pull before you push, or to use MagicSync for differential deployments.
MagicSync: Differential Deployment Without the Pain
MagicSync is Flywheel’s differential file sync engine built into Local. Instead of uploading the entire wp-content directory every time you push, MagicSync compares file checksums between your local environment and the remote Flywheel server, then only transfers the files that have actually changed. This turns a 10-minute full push into a 15-second incremental deployment for most changes.
How MagicSync Determines Changes
MagicSync maintains a manifest of file paths and their MD5 checksums for both the local and remote environments. When you initiate a push, it fetches the remote manifest, diffs it against the local manifest, and produces three lists: files to upload (new or modified), files to delete (removed locally but still present remotely), and files to skip (unchanged). Only the upload and delete lists get acted on.
The checksum comparison means that MagicSync does not care about file timestamps. If you rebuild your CSS and the output happens to be identical, it will not re-upload the file. Conversely, if you touch a file without changing its contents, it still will not re-upload because the checksum has not changed. This behavior is more reliable than timestamp-based sync tools like rsync with the default settings.
MagicSync in Practice
When working on a theme, you might edit three Sass partials, recompile CSS, and update a PHP template. A full push would upload the entire theme directory (and every plugin, and every upload). MagicSync uploads only the changed CSS output file and the modified PHP template. If you have 200 MB of uploads on the site and changed two files totaling 45 KB, MagicSync saves you from transferring the other 199.96 MB.
MagicSync only handles files, not the database. If your changes include database modifications (new posts, updated options, widget configurations), you still need to push the database separately using the standard push dialog. A common agency workflow is to use MagicSync for all theme and plugin file deployments, and reserve full database pushes for the initial setup or major structural changes.
Exclusions and Ignore Rules
MagicSync respects a set of default exclusions: node_modules, .git, .DS_Store, Thumbs.db, and other common development artifacts. You can add custom exclusion rules inside Local’s preferences. If your theme includes a large design assets directory that should not be deployed, you add that path to the exclusion list and MagicSync will skip it entirely.
One useful pattern is to exclude your source Sass or PostCSS files and only deploy the compiled CSS. This keeps the production server clean and avoids exposing source files. Your exclusion list might look like this:
node_modules/
.git/
.gitignore
src/scss/
src/postcss/
*.map
package.json
package-lock.json
webpack.config.js
gulpfile.js
Blueprint Creation and Management
Blueprints are Flywheel’s site templating system. A Blueprint captures an entire WordPress installation (themes, plugins, database content, settings) as a reusable template. When you create a new site from a Blueprint, Flywheel provisions a fresh installation pre-loaded with everything from the template. For agencies that build dozens of sites a year on similar foundations, Blueprints eliminate hours of repetitive setup.
Creating a Blueprint
You create a Blueprint from any existing Flywheel site. In the Flywheel dashboard, navigate to the site you want to use as a template, open the Advanced tab, and click “Create Blueprint.” Flywheel snapshots the entire site: all files in wp-content, the full database, and the WordPress core version. The Blueprint appears in your account’s Blueprint library, available to anyone on your team.
A well-constructed agency Blueprint typically includes:
- A starter theme (your agency’s base theme or a framework like Sage, Flavor, or Flavor).
- Essential plugins already installed and activated: Yoast SEO or Rank Math, Gravity Forms or WPForms, ACF Pro, WP Migrate DB Pro, a security plugin like Wordfence or Sucuri, and a performance plugin like WP Rocket or Perfmatters.
- WordPress settings already configured: permalink structure set to /%postname%/, timezone set to your default, discussion settings locked down, default widgets removed, sample content deleted.
- A custom must-use plugin (mu-plugin) that sets agency defaults: disabling XML-RPC, removing the WordPress version from headers, disabling file editing in the admin, and setting auto-update policies.
- Placeholder pages (Home, About, Services, Contact, Privacy Policy, Terms) with basic content structure so the site is not empty when the client first sees it.
Blueprint Maintenance
Blueprints are static snapshots. They do not auto-update. If you create a Blueprint in January with WordPress 6.1 and Yoast 19.0, and you spin up a new site from it in June, that site arrives with WordPress 6.1 and Yoast 19.0. Your first task after creating the site is to run updates.
Smart agencies maintain a “Blueprint source site” on Flywheel that exists solely for this purpose. Every month, you log into that site, run all updates (core, plugins, themes), test that nothing is broken, and then re-create the Blueprint from the updated site. This keeps your Blueprint reasonably current and cuts the post-creation update burden from twenty plugins down to one or two that released updates in the last few days.
You cannot edit a Blueprint directly. The only workflow is: update the source site, delete the old Blueprint, create a new one. There is no versioning, no diff view, and no way to merge two Blueprints. Keep your source site clean and well-documented so you always know what is in the Blueprint.
Limitations You Need to Know
Blueprints have several hard limitations that can trip up agency workflows:
- No multisite Blueprints. You cannot create a Blueprint from a WordPress multisite installation. If your agency runs a multisite network, Blueprints are off the table entirely. You will need to script your own provisioning system or use WP-CLI export/import commands.
- No multisite site copying. Even outside of Blueprints, Flywheel does not support copying individual subsites from a multisite network. Each subsite would need to be exported with WP-CLI and imported into a standalone installation manually.
- No partial Blueprints. You cannot create a Blueprint that only includes plugins, or only includes the theme. It is an all-or-nothing snapshot. If you want modular setups (Theme A with Plugin Set 1, Theme B with Plugin Set 2), you need separate Blueprints for each combination.
- Storage limits. Blueprints count against your account storage. A Blueprint with 500 MB of uploads will take 500 MB of space. Trim your uploads directory before creating the Blueprint.
- No Blueprint API. There is no REST API or CLI command to create, list, or deploy Blueprints programmatically. Everything goes through the web dashboard. If you need automated provisioning, you will need to look at the Flywheel API (which has limited Blueprint support) or build a custom solution.
Git-Based Deployment via SSH and GitHub Actions
While Local’s push/pull and MagicSync cover most deployment scenarios, agencies with established Git workflows often want to deploy directly from a repository. Flywheel supports SSH access on Growth and higher plans, which opens the door to Git-based deployments, either manually or through CI/CD pipelines like GitHub Actions.
Enabling SSH and Adding Your Key
SSH access is not enabled by default on Flywheel. You need to contact Flywheel support to have it activated for your site. Once enabled, you get an SFTP/SSH hostname, port, and username. You then need to add your public SSH key through the Flywheel dashboard under your user profile.
Here is where the first gotcha appears: Flywheel requires RSA keys in PEM format. If you generated your key with a recent version of OpenSSH (8.0+), the default format is the OpenSSH format, not PEM. Flywheel will silently reject it. You need to generate or convert your key specifically:
# Generate a new RSA key in PEM format
ssh-keygen -t rsa -b 4096 -m PEM -f ~/.ssh/flywheel_rsa -C "[email protected]"
# If you already have a key and need to convert it to PEM
ssh-keygen -p -m PEM -f ~/.ssh/flywheel_rsa
The -m PEM flag is the critical piece. Without it, the key header will read -----BEGIN OPENSSH PRIVATE KEY----- instead of -----BEGIN RSA PRIVATE KEY-----, and Flywheel’s SSH daemon will not accept it.
Copy the public key contents and paste them into the SSH public key field in your Flywheel dashboard profile:
cat ~/.ssh/flywheel_rsa.pub
Test the connection:
ssh -i ~/.ssh/flywheel_rsa -p 22222 [email protected]
The default SSH port for Flywheel is 22222, not the standard 22. This catches people off guard regularly.
Repository Structure for Flywheel Deployments
Most agencies version-control only the theme (and sometimes custom plugins), not the entire WordPress installation. A typical repository structure looks like this:
my-client-theme/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── inc/
│ ├── custom-post-types.php
│ ├── shortcodes.php
│ └── theme-setup.php
├── template-parts/
│ ├── content.php
│ ├── content-page.php
│ └── hero.php
├── functions.php
├── style.css
├── header.php
├── footer.php
├── index.php
├── single.php
├── page.php
├── package.json
├── webpack.config.js
└── .gitignore
The .gitignore should exclude compiled artifacts that your CI pipeline rebuilds, or include them if your deployment just copies files. There is no single correct answer here. Some teams commit compiled CSS/JS so the deployment is a simple rsync. Others keep source files in the repo and run a build step in CI. Pick one approach and stick with it across all client projects.
GitHub Actions Deployment Workflow
Here is a complete GitHub Actions workflow that builds a theme and deploys it to Flywheel via rsync over SSH:
name: Deploy to Flywheel
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build assets
run: npm run build
- name: Setup SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.FLYWHEEL_SSH_KEY }}" > ~/.ssh/flywheel_rsa
chmod 600 ~/.ssh/flywheel_rsa
ssh-keyscan -p 22222 sftp.flywheelsites.com >> ~/.ssh/known_hosts 2>/dev/null
- name: Deploy via rsync
run: |
rsync -avz --delete \
--exclude='.git' \
--exclude='.github' \
--exclude='node_modules' \
--exclude='.gitignore' \
--exclude='package.json' \
--exclude='package-lock.json' \
--exclude='webpack.config.js' \
--exclude='src/' \
-e "ssh -i ~/.ssh/flywheel_rsa -p 22222 -o StrictHostKeyChecking=no" \
./ ${{ secrets.FLYWHEEL_SSH_USER }}@sftp.flywheelsites.com:/www/wp-content/themes/my-client-theme/
Store your PEM-format private key as a GitHub Actions secret named FLYWHEEL_SSH_KEY. Store the SFTP username as FLYWHEEL_SSH_USER. The rsync command uses --delete to remove files from the server that no longer exist in the repository, keeping the deployment clean.
A few implementation notes:
- The remote path
/www/wp-content/themes/my-client-theme/is relative to the SSH user’s root. On Flywheel, the site root is/www/, so wp-content sits at/www/wp-content/. - If you are deploying a plugin instead of a theme, change the remote path to
/www/wp-content/plugins/my-plugin/. - The
ssh-keyscanstep pre-populates the known_hosts file so rsync does not prompt for host key verification. The-o StrictHostKeyChecking=noflag is a fallback. - If your build step generates source maps, add
--exclude='*.map'to the rsync excludes. Source maps should not exist on production.
Deploying Multiple Sites from a Monorepo
Some agencies use a monorepo containing themes for multiple clients. In that case, you can use GitHub Actions path filters and matrix builds:
name: Deploy Client Themes
on:
push:
branches:
- main
paths:
- 'clients/**'
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- id: set-matrix
run: |
CHANGED=$(git diff --name-only HEAD~1 HEAD | grep '^clients/' | cut -d'/' -f2 | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "matrix={\"client\":$CHANGED}" >> $GITHUB_OUTPUT
deploy:
needs: detect-changes
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
steps:
- uses: actions/checkout@v3
- name: Deploy ${{ matrix.client }}
run: |
# Load client-specific config
source clients/${{ matrix.client }}/.deploy-config
mkdir -p ~/.ssh
echo "${{ secrets.FLYWHEEL_SSH_KEY }}" > ~/.ssh/flywheel_rsa
chmod 600 ~/.ssh/flywheel_rsa
rsync -avz --delete \
--exclude='.git' \
--exclude='node_modules' \
--exclude='.deploy-config' \
-e "ssh -i ~/.ssh/flywheel_rsa -p 22222" \
clients/${{ matrix.client }}/theme/ \
${DEPLOY_USER}@sftp.flywheelsites.com:/www/wp-content/themes/${DEPLOY_THEME}/
Each client directory contains a .deploy-config file with the SFTP username and theme directory name:
DEPLOY_USER="your-sftp-user-for-client-a"
DEPLOY_THEME="client-a-theme"
This pattern scales well. Add a new client by creating their directory, dropping in the .deploy-config file, and pushing. The CI pipeline picks it up automatically.
FlyCache and CDN: Understanding Server-Level Caching
Flywheel runs a server-level full-page cache called FlyCache. It sits in front of WordPress as a reverse proxy, serving cached HTML to visitors without hitting PHP at all. Understanding how FlyCache works is important because it affects deployment workflows, debugging, and plugin compatibility.
How FlyCache Works
When a request comes in for a page, FlyCache checks if it has a cached copy. If it does and the cache is still valid, it serves the cached HTML directly. The response header will include x-cache: HIT. If there is no cached copy or the cache has expired, the request passes through to PHP/WordPress, the response is generated dynamically, and FlyCache stores a copy for future requests. That response gets x-cache: MISS.
You can see FlyCache behavior in your browser’s developer tools or with curl:
curl -I https://yourclientsite.com/ 2>/dev/null | grep -i 'x-cache\|x-served-by\|cache-control'
Typical output for a cached page:
cache-control: public, max-age=31536000
x-cache: HIT
x-served-by: Starter
Cache Exclusions
FlyCache automatically excludes certain requests from caching:
- Any request from a logged-in user (detected by the
wordpress_logged_in_*cookie). - POST requests.
- Requests with query strings (by default, though this is configurable).
- WooCommerce cart, checkout, and account pages (detected by WooCommerce-specific cookies).
- Any page that sets a
Cache-Control: no-cacheorCache-Control: privateheader.
This means that when you are logged into WordPress and checking your changes after a deployment, you are always seeing the uncached, dynamic version. If you want to verify that the cache is working correctly for anonymous visitors, use an incognito window or curl without cookies.
Clearing the Cache
You can clear FlyCache from three places:
- Flywheel Dashboard: Navigate to the site, click the “Flush Cache” button in the top bar.
- WordPress Admin: Flywheel installs a must-use plugin that adds a “Clear Cache” button to the WordPress admin bar.
- WP-CLI: SSH in and run
wp cache flush. Note that this flushes the object cache, not FlyCache directly. To flush FlyCache via CLI, you usecurl -X PURGE https://yourclientsite.com/from the server, or use the Flywheel API.
After deploying theme changes via Git or rsync, you should always flush the cache. Add a cache-clear step to your CI pipeline:
- name: Flush FlyCache
run: |
ssh -i ~/.ssh/flywheel_rsa -p 22222 \
${{ secrets.FLYWHEEL_SSH_USER }}@sftp.flywheelsites.com \
"cd /www && wp cache flush"
CDN Behavior
Flywheel includes a CDN powered by Fastly on all plans. The CDN caches static assets (images, CSS, JS, fonts) at edge locations globally. When you deploy new CSS or JS files, the CDN may serve stale versions until its cache expires. The simplest way to handle this is to use cache-busting filenames or query strings in your asset enqueues:
// In functions.php
wp_enqueue_style(
'theme-style',
get_stylesheet_directory_uri() . '/dist/css/main.css',
array(),
filemtime(get_stylesheet_directory() . '/dist/css/main.css')
);
wp_enqueue_script(
'theme-script',
get_stylesheet_directory_uri() . '/dist/js/main.js',
array(),
filemtime(get_stylesheet_directory() . '/dist/js/main.js'),
true
);
Using filemtime() as the version parameter means the query string changes every time the file is modified, which busts the CDN cache for that specific asset without requiring a full CDN purge.
If you need to purge the CDN entirely, the Flywheel dashboard has a “Flush CDN” option separate from the FlyCache flush. You can also toggle the CDN on and off per site.
Client Billing Transfer and White-Label Workflows
One of Flywheel’s strongest agency features is the billing transfer system. You can build a client’s site on your agency Flywheel account, and when the site is ready to launch, transfer the billing responsibility to the client. The client gets their own Flywheel account and pays their own hosting bill. Your agency retains collaborator access to manage the site technically, but the client owns the billing relationship with Flywheel.
How Billing Transfer Works
The transfer process goes like this:
- In the Flywheel dashboard, navigate to the site and open the “Billing” tab.
- Click “Transfer Billing” and enter the client’s email address.
- Flywheel sends the client an invitation email. If the client does not already have a Flywheel account, they are prompted to create one.
- The client accepts the invitation, selects a hosting plan, and enters their payment information.
- Billing transfers immediately. The site is removed from your agency’s bill and appears on the client’s bill.
- Your agency remains a collaborator on the site with full technical access.
The transfer does not cause any downtime. The site stays on the same server, keeps the same IP address, and retains all its files and database content. The only thing that changes is who Flywheel charges.
White-Label Considerations
Flywheel does not offer a full white-label reseller program where you can completely hide the Flywheel brand. When you transfer billing, the client interacts directly with Flywheel for their invoices, account management, and support. They will see the Flywheel dashboard, Flywheel branding, and Flywheel support channels.
If you want to keep clients away from the hosting layer entirely, do not transfer billing. Instead, keep the sites on your agency account and bill clients through your own invoicing system. This approach gives you full control over the client relationship but means you absorb the hosting cost and need to factor it into your retainer or project pricing.
Some agencies take a hybrid approach: they transfer billing for larger clients who want direct control of their hosting, and keep smaller clients on the agency account where the overhead of explaining Flywheel’s interface is not worth it.
Growth Plan and Bulk Billing
Flywheel’s Growth plan is designed for agencies managing many sites. It offers bulk pricing (the per-site cost drops as you add more sites), plus additional features like SSH access, site cloning, and priority support. If your agency keeps most sites on your own account rather than transferring billing, the Growth plan’s economies of scale make it significantly cheaper per site than individual plans.
The math works like this: if you have 30 client sites on a Growth plan at $25/site/month and you charge each client $50/month for hosting as part of a care plan, you clear $750/month in pure margin. That recurring revenue adds up fast and creates a predictable income stream independent of project work.
Staging Environments and Testing Workflows
Every Flywheel site includes a free staging environment. The staging site is a complete copy of the production site, running on the same server but with a separate database and file system. The staging URL follows the pattern staging-sitename.flywheelsites.com.
Creating and Managing Staging
You create a staging environment from the Flywheel dashboard by clicking “Create Staging” on the site overview page. Flywheel copies the production database and files to the staging environment, performing a search-replace on the domain. This usually takes a few minutes depending on the site size.
Key characteristics of staging on Flywheel:
- Password protected by default. Staging sites are not publicly accessible. Visitors are prompted with an HTTP authentication dialog. You can share the credentials with the client for review.
- Search engines blocked. Staging sites include a
noindex, nofollowrobots meta tag and anX-Robots-Tag: noindexheader. Even if someone leaks the URL, search engines will not index it. - Email disabled. Staging sites do not send transactional emails by default. This prevents embarrassing situations where a staging environment sends real emails to real customers. If you need email for testing, use a plugin like WP Mail SMTP pointed at a service like Mailtrap.
- SSL included. Staging sites get their own SSL certificate automatically. No configuration needed.
- Caching reduced. FlyCache still runs on staging, but with a shorter TTL. For testing purposes, you may want to flush the cache frequently or add a query string to bypass it.
Pushing Staging to Production
When you are satisfied with the changes on staging, you can push staging to production from the Flywheel dashboard. The push dialog gives you the same three options as Local: Files, Database, or both. You also get a choice between “Full push” (replaces everything) and “Selective push” (merges specific tables or directories).
The selective push option is useful when you only want to deploy theme changes without overwriting the production database. This is the safer option for sites with active content creation, where editors might have published new posts since the staging environment was created.
A safe agency workflow for staging deployments:
- Create staging from production (fresh copy).
- Deploy your changes to staging (via Local push, Git, or manual upload).
- Test thoroughly on staging. Check forms, checkout flows, responsive layouts, third-party integrations.
- Send the staging URL and credentials to the client for approval.
- Once approved, push staging to production with “Files only” to avoid overwriting production content.
- Flush the production cache.
- Spot-check the production site.
Staging Limitations
You get one staging environment per site. There is no way to create multiple staging environments for different feature branches or different team members. If your workflow requires parallel staging (testing Feature A and Feature B independently), you will need to use Local for the parallel testing and only push to Flywheel staging for the final client review.
Staging environments also do not support scheduled tasks independently of production. If you have WP-Cron jobs or Action Scheduler tasks running, they will fire on staging too, which can create duplicate data or trigger external API calls. Disable cron on staging by adding this to the staging site’s wp-config.php:
define('DISABLE_WP_CRON', true);
Collaboration Features for Agency Teams
Flywheel’s collaboration system lets you invite team members and clients to sites with different permission levels. This is essential for agencies where developers, designers, project managers, and clients all need access to different parts of the hosting environment.
Roles and Permissions
Flywheel offers three collaboration levels:
- Owner: Full control, including billing, plan changes, and the ability to delete the site. There can be only one owner per site.
- Admin collaborator: Can manage site settings, create/push staging, add other collaborators, access SFTP/SSH, and view site analytics. Cannot change billing or delete the site.
- Collaborator: Can access SFTP/SSH and the WordPress admin. Cannot change Flywheel site settings, manage staging, or add other collaborators.
For a typical agency setup, the agency owner or lead developer is the Owner. Senior developers get Admin collaborator access. Junior developers and contractors get basic Collaborator access. Clients get Collaborator access if they need to log into the Flywheel dashboard at all, though most clients never need to.
Organization Accounts
Flywheel supports organization accounts that group multiple sites and team members under one umbrella. When a new developer joins your agency, you add them to the organization, and they automatically get access to all organization sites at the permission level you set. When someone leaves, you remove them from the organization and their access to all sites is revoked instantly.
Without organization accounts, you would need to add and remove collaborators from each site individually. For an agency managing 30+ sites, that is a significant administrative burden every time someone joins or leaves the team.
SFTP Credentials Per Collaborator
Each collaborator gets their own SFTP credentials. This is a security advantage over shared credentials because you can track who accessed what, and revoking one person’s access does not require changing credentials for everyone else. When a freelancer finishes a project, you remove them as a collaborator, and their SFTP credentials stop working immediately.
For SSH key-based access, each collaborator uploads their own public key to their Flywheel profile. The key is tied to their user account, not to the site. If they are a collaborator on five sites, the same key works for all five.
Flywheel vs. WP Engine: Post-Acquisition Realities
WP Engine acquired Flywheel in June 2019. Since the acquisition, both platforms have continued to operate as separate products with separate dashboards, pricing, and feature sets. But the lines have been blurring, and understanding where the two platforms diverge helps you decide which to use for different client scenarios.
Dashboard and User Experience
Flywheel’s dashboard remains simpler and more visually polished than WP Engine’s. Flywheel was designed for agencies and freelancers who want to get things done quickly without wading through enterprise-grade options panels. WP Engine’s dashboard has more features but also more complexity, reflecting its focus on larger organizations and enterprise clients.
For day-to-day agency work (creating sites, managing staging, transferring billing), Flywheel’s interface is faster to navigate. For advanced needs (custom PHP version selection, detailed access logs, advanced redirect rules), WP Engine provides more controls.
Local Development
Local integrates with both Flywheel and WP Engine. The push/pull workflow works with either platform. MagicSync, however, was built for Flywheel and works best with Flywheel sites. WP Engine integration in Local uses a different sync mechanism that is functional but less refined.
Performance Infrastructure
Both platforms now share underlying infrastructure (Google Cloud Platform), but they configure it differently. WP Engine uses its proprietary EverCache technology. Flywheel uses FlyCache. In practice, performance is similar for most WordPress sites. The difference shows up on high-traffic WooCommerce sites or sites with complex uncached queries, where WP Engine’s more granular cache controls and dedicated environment options give it an edge.
Pricing and Plans
Flywheel’s pricing tends to be lower at the starter level and competitive at the agency/growth level. WP Engine’s pricing is higher but includes features like Genesis themes, automated plugin updates with visual regression testing (Smart Plugin Manager), and more generous staging options.
A direct comparison at the time of writing:
- Single site, basic: Flywheel starts at $15/month. WP Engine starts at $20/month.
- Agency/Growth plan: Flywheel’s Growth plan starts around $115/month for 10 sites. WP Engine’s equivalent plan starts around $96/month for 10 sites but with different feature bundles.
- Enterprise: Both offer custom pricing. WP Engine has a more established enterprise sales team and SLA options.
Pricing changes frequently, so check current rates before making decisions. The point is that the cost difference between the two platforms is small enough that features and workflow preferences should drive your choice, not price.
Plugin Restrictions
Both Flywheel and WP Engine maintain lists of disallowed plugins. These are typically caching plugins (because the server-level cache makes them redundant and they can cause conflicts), backup plugins that cause server load issues, and plugins with known security vulnerabilities.
WP Engine’s disallowed list is longer and more strictly enforced. Flywheel’s list is shorter and the enforcement is lighter. If you have a plugin that works on Flywheel but not on WP Engine, this is usually why. Always check the disallowed plugin list before recommending a hosting platform for a client with specific plugin requirements.
Which Should Your Agency Choose?
Use Flywheel when your agency primarily builds marketing sites, portfolios, and small to mid-size business sites. The workflow is streamlined, the pricing is competitive, and the billing transfer feature is best-in-class for agencies that hand off sites to clients.
Use WP Engine when the client needs enterprise-grade SLAs, complex WooCommerce setups, or specific features like Smart Plugin Manager. WP Engine is also the better choice when the client’s IT department will manage the hosting long-term and needs the more granular controls that WP Engine provides.
Many agencies use both. They keep Flywheel for the bulk of their client work and move specific sites to WP Engine when the requirements demand it. Since both platforms use the same parent company’s infrastructure, migrations between them are straightforward.
Building an Efficient Agency Onboarding Process with Flywheel
Everything covered in this guide comes together in the agency onboarding process: the sequence of steps from “new client signed” to “site is live and billing is transferred.” A well-optimized onboarding process using Flywheel’s tools can compress this timeline from weeks to days.
Step 1: Spin Up from Blueprint
The moment a new client signs, create their site from your agency Blueprint. This gives you a WordPress installation with your starter theme, all essential plugins, correct settings, and placeholder content. Total time: about 3 minutes.
# No CLI for Blueprints, but here's the equivalent manual setup if needed
# Create site via Flywheel dashboard using Blueprint
# Then pull to Local for development:
# In Local: Click "Pull from Flywheel" > Select new site > Pull Files + Database
Step 2: Pull to Local and Begin Development
Pull the Blueprint-created site into Local. Initialize a Git repository for the theme. Set up your development tooling (Webpack, Vite, Tailwind, whatever your stack uses). Make your first commit.
cd ~/Local\ Sites/client-name/app/public/wp-content/themes/starter-theme
# Initialize Git
git init
git add .
git commit -m "Initial commit from agency Blueprint"
# Set up remote
git remote add origin [email protected]:youragency/client-name-theme.git
git push -u origin main
# Install dev dependencies
npm install
# Start watching for changes
npm run watch
Step 3: Development Phase
Build the site locally. Commit regularly. Use feature branches for major sections (header, homepage, inner pages, WooCommerce customization). Merge to main when features are complete. The entire development phase happens in Local, disconnected from the Flywheel server.
If multiple developers are working on the site, each pulls the Local site from Flywheel independently, clones the theme repo, and works on their own branches. This avoids the conflicts that arise when multiple developers push to the same Flywheel staging environment.
Step 4: Deploy to Staging for Client Review
When the site is ready for the client to see, create a staging environment on Flywheel and deploy your theme either via MagicSync (from Local) or via your GitHub Actions pipeline (from the repo). Push the database from Local to staging so the client sees the content structure you have built.
# Option A: Push from Local
# In Local: Click "Push to Flywheel" > Select staging > Push Files + Database
# Option B: Deploy via Git (if CI is set up)
git push origin main
# GitHub Actions deploys to staging automatically
# Then push database from Local separately for content
Share the staging URL and HTTP authentication credentials with the client. Walk them through the site on a call or in a Loom video. Collect feedback, iterate, and re-deploy until the client approves.
Step 5: Launch
Once approved, push staging to production. Update the domain to the client’s production domain in the Flywheel dashboard. Update DNS records at the client’s registrar to point to Flywheel’s IP address. Flywheel provisions a free SSL certificate via Let’s Encrypt automatically once DNS propagates.
Post-launch checklist:
- Verify SSL is working (green padlock, no mixed content warnings).
- Test all forms (contact, newsletter, checkout if applicable).
- Verify analytics tracking is firing.
- Submit the sitemap to Google Search Console.
- Run a Lighthouse audit and address any critical issues.
- Set up uptime monitoring (Pingdom, UptimeRobot, or similar).
- Configure backup schedules (Flywheel includes nightly backups, but verify).
Step 6: Transfer Billing
If the client will own their hosting relationship, initiate the billing transfer. Send the client a heads-up email explaining what to expect (they will get an email from Flywheel, need to create an account if they do not have one, and enter payment info). Once the transfer completes, verify that your agency still has collaborator access.
Step 7: Ongoing Maintenance
After launch, your workflow shifts from development to maintenance. Use Flywheel’s built-in nightly backups as a safety net. Deploy plugin and core updates through staging first (create staging, update on staging, test, push to production). For theme changes, use your Git pipeline so every change is tracked, reviewable, and reversible.
Set up a monthly maintenance routine:
- Create staging from production.
- Run all updates (WordPress core, plugins, themes) on staging.
- Run automated visual regression tests if you have them (tools like BackstopJS or Percy).
- Manually test critical user flows (homepage load, contact form, checkout).
- Push staging to production (files + database) if all tests pass.
- Flush cache.
- Verify production.
# Example BackstopJS config for visual regression testing
# backstop.json
{
"id": "client-site",
"viewports": [
{ "label": "phone", "width": 375, "height": 812 },
{ "label": "tablet", "width": 768, "height": 1024 },
{ "label": "desktop", "width": 1440, "height": 900 }
],
"scenarios": [
{
"label": "Homepage",
"url": "https://staging-clientsite.flywheelsites.com",
"referenceUrl": "https://clientsite.com",
"selectors": ["document"],
"misMatchThreshold": 0.1
},
{
"label": "Contact Page",
"url": "https://staging-clientsite.flywheelsites.com/contact/",
"referenceUrl": "https://clientsite.com/contact/",
"selectors": ["document"],
"misMatchThreshold": 0.1
}
],
"engine": "puppeteer"
}
# Run the visual regression tests
npx backstop reference # Capture production screenshots as baseline
npx backstop test # Compare staging against baseline
Documenting the Process
Create an internal agency wiki page or Notion template that documents your Flywheel onboarding process step by step. Include the Blueprint name, the GitHub repo template, the CI/CD configuration, the post-launch checklist, and the maintenance routine. When a new team member joins, they should be able to follow this document and onboard a client site without asking questions.
The template should also include client-facing documentation: how to log into WordPress, how to create pages and posts, how to upload media, and who to contact for support. Generate this documentation once and reuse it across clients, customizing only the site-specific details.
Advanced Patterns and Operational Tips
Beyond the core workflow, several operational patterns will save your agency time and prevent common mistakes.
WP-CLI on Flywheel
Flywheel supports WP-CLI over SSH. This means you can run database operations, manage plugins, and perform bulk content operations without logging into the WordPress admin or the Flywheel dashboard.
# SSH into Flywheel and run WP-CLI commands
ssh -i ~/.ssh/flywheel_rsa -p 22222 [email protected]
# Once connected:
cd /www
wp plugin list
wp plugin update --all
wp core update
wp search-replace 'http://oldsite.com' 'https://newsite.com' --dry-run
wp db export backup-$(date +%Y%m%d).sql
You can also pipe WP-CLI commands through SSH directly without an interactive session:
# Run a single WP-CLI command remotely
ssh -i ~/.ssh/flywheel_rsa -p 22222 [email protected] "cd /www && wp plugin list --format=csv"
# Export the remote database to your local machine
ssh -i ~/.ssh/flywheel_rsa -p 22222 [email protected] "cd /www && wp db export -" > remote-backup.sql
Handling Multisite (Without Blueprints)
Since Blueprints do not support multisite, you need an alternative provisioning strategy for WordPress networks. The approach that works best on Flywheel is a shell script that sets up a base multisite installation using WP-CLI:
#!/bin/bash
# provision-multisite.sh
# Run this after SSHing into a fresh Flywheel site
cd /www
# Install WordPress multisite
wp core multisite-install \
--title="Client Network" \
--admin_user=admin \
--admin_password="$(openssl rand -base64 24)" \
[email protected] \
--subdomains
# Install common plugins across the network
wp plugin install wordpress-seo --activate-network
wp plugin install advanced-custom-fields --activate-network
wp plugin install wordfence --activate-network
# Create subsites
wp site create --slug=blog --title="Blog"
wp site create --slug=shop --title="Shop"
wp site create --slug=docs --title="Documentation"
echo "Multisite provisioning complete."
Store this script in your agency’s internal tools repository and customize it per client. It is not as elegant as clicking “Create from Blueprint,” but it is reproducible and version-controlled.
Migrating to Flywheel from Other Hosts
Flywheel offers free migrations for new accounts. You can also migrate sites yourself using Local as an intermediary:
- Export the database and files from the old host (via cPanel, SSH, or a migration plugin like Duplicator or WP Migrate DB Pro).
- Import the site into Local (Local can import SQL dumps and zip archives).
- Test the site locally to make sure everything works.
- Push from Local to the Flywheel production site.
- Update DNS to point to Flywheel.
The Local intermediary step is useful because it lets you fix any issues (broken image paths, plugin conflicts, PHP version incompatibilities) in a safe environment before touching the live site.
Performance Monitoring
Flywheel provides basic performance metrics in the dashboard: bandwidth usage, visitor counts, and storage consumption. For deeper performance monitoring, install Query Monitor on the site. It shows you database query times, HTTP API calls, hooks fired, and memory usage. Since Query Monitor only runs for logged-in admins, it does not affect site performance for visitors.
# Install Query Monitor via WP-CLI
ssh -i ~/.ssh/flywheel_rsa -p 22222 [email protected] \
"cd /www && wp plugin install query-monitor --activate"
For front-end performance, combine Lighthouse CI with your GitHub Actions pipeline to catch regressions before they reach production:
- name: Run Lighthouse CI
uses: treosh/lighthouse-ci-action@v10
with:
urls: |
https://staging-clientsite.flywheelsites.com/
https://staging-clientsite.flywheelsites.com/about/
budgetPath: .github/lighthouse-budget.json
uploadArtifacts: true
Security Hardening on Flywheel
Flywheel handles server-level security (firewalls, intrusion detection, malware scanning, DDoS protection). Your agency’s responsibility is application-level security. A must-use plugin in your Blueprint should enforce these rules:
<?php
/**
* Plugin Name: Agency Security Defaults
* Description: Enforces baseline security settings across all agency sites.
*/
// Disable file editing in WP admin
define('DISALLOW_FILE_EDIT', true);
// Disable XML-RPC (unless needed for Jetpack or mobile apps)
add_filter('xmlrpc_enabled', '__return_false');
// Remove WordPress version from headers and feeds
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');
// Disable REST API user enumeration for non-authenticated users
add_filter('rest_authentication_errors', function ($result) {
if (true === $result || is_wp_error($result)) {
return $result;
}
if (!is_user_logged_in()) {
$route = untrailingslashit($GLOBALS['wp']->query_vars['rest_route'] ?? '');
if (strpos($route, '/wp/v2/users') === 0) {
return new WP_Error(
'rest_cannot_access',
'Restricted.',
array('status' => 401)
);
}
}
return $result;
});
// Force strong passwords (WordPress 4.3+)
add_action('user_profile_update_errors', function ($errors, $update, $user) {
if (isset($_POST['pass1']) && strlen($_POST['pass1']) < 12) {
$errors->add('weak_password', 'Password must be at least 12 characters.');
}
}, 10, 3);
// Limit login attempts (basic implementation)
add_filter('authenticate', function ($user, $username, $password) {
if (empty($username)) return $user;
$ip = $_SERVER['REMOTE_ADDR'];
$transient_key = 'login_attempts_' . md5($ip);
$attempts = get_transient($transient_key) ?: 0;
if ($attempts >= 5) {
return new WP_Error('too_many_attempts', 'Too many failed login attempts. Try again in 15 minutes.');
}
return $user;
}, 30, 3);
add_action('wp_login_failed', function ($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$transient_key = 'login_attempts_' . md5($ip);
$attempts = get_transient($transient_key) ?: 0;
set_transient($transient_key, $attempts + 1, 15 * MINUTE_IN_SECONDS);
});
Drop this file in wp-content/mu-plugins/ in your Blueprint source site. It will be active on every site created from the Blueprint, and since it is a must-use plugin, it cannot be deactivated from the WordPress admin.
Automating Blueprint Updates
While Flywheel does not offer a CLI or API for Blueprint management, you can automate the Blueprint source site updates with a cron job or scheduled GitHub Action that SSHes into the source site and runs updates:
name: Update Blueprint Source Site
on:
schedule:
- cron: '0 9 1 * *' # First of every month at 9 AM UTC
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.BLUEPRINT_SSH_KEY }}" > ~/.ssh/flywheel_rsa
chmod 600 ~/.ssh/flywheel_rsa
- name: Update WordPress core and plugins
run: |
ssh -i ~/.ssh/flywheel_rsa -p 22222 \
${{ secrets.BLUEPRINT_SSH_USER }}@sftp.flywheelsites.com \
"cd /www && wp core update && wp plugin update --all && wp theme update --all"
- name: Notify team
run: |
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Blueprint source site updated. Please test and recreate the Blueprint."}' \
${{ secrets.SLACK_WEBHOOK_URL }}
After the automated update runs, a team member still needs to log in, verify everything works, and manually recreate the Blueprint. The automation handles the tedious update step; the human handles the verification and Blueprint creation.
Putting It All Together
The Flywheel agency workflow, when fully set up, looks like this:
- Blueprints handle the initial site provisioning, giving you a consistent starting point for every project.
- Local provides the development environment, isolated from production, with full control over PHP versions and server configuration.
- MagicSync handles file deployments from Local to Flywheel efficiently, transferring only what changed.
- Git + GitHub Actions provide version control, code review, and automated deployment pipelines for teams that need them.
- Staging gives clients a safe preview environment and gives developers a pre-production testing ground.
- FlyCache and CDN handle performance at the server level, so you rarely need to think about caching plugins.
- Billing transfer lets you hand off hosting costs to clients cleanly, or keep them on your account for recurring revenue.
- Collaboration features manage team access with appropriate permission levels, and organization accounts simplify team management at scale.
No single tool in this stack is revolutionary on its own. What makes Flywheel effective for agencies is how these tools integrate with each other. The path from “new client” to “live site” flows through a coherent sequence of tools that all talk to each other, rather than a collection of disconnected services stitched together with custom scripts.
The limitations are real and worth acknowledging. No multisite Blueprint support means multisite agencies need workarounds. One staging environment per site means parallel feature testing stays in Local. No Blueprint API means provisioning at scale still requires manual dashboard clicks. And the WP Engine acquisition means Flywheel’s long-term roadmap is tied to a larger company’s strategic priorities.
But for agencies building 10 to 100 WordPress sites a year, the Flywheel workflow remains one of the most efficient options available. The combination of Local for development, Blueprints for templating, MagicSync for deployment, and billing transfer for client handoff solves the core operational problems that eat up agency time. Master these tools, build your processes around them, and the operational side of running a WordPress agency becomes significantly less painful.
James Whitfield
Enterprise WordPress architect. Manages multisite networks for university systems and media companies. Expert in scaling WordPress to hundreds of sites.