Automation 9 min read October 17, 2026

How to Run Playwright on Fly.io for Browser Automation

Learn how to deploy and run Playwright on Fly.io to handle browser automation tasks efficiently while reducing costs and avoiding vendor lock-in.

Key Takeaways

What you'll learn from this guide

Containerizing Playwright allows you to escape high premiums charged by managed scraping services.

Fly.io provides granular control over the browser environment and compute resources.

Memory management is the biggest factor when running multiple concurrent browser sessions.

Using Docker allows for consistent environments between your local machine and the cloud.

Always use persistent volumes if your automation requires long-lived browser sessions or cache.

Horizontal scaling on Fly.io is more cost-effective than scaling expensive, pre-built automation nodes.

Why Run Playwright on Fly.io Instead of Managed Services?

If you are tired of watching your automation bills spike every time you scale your browser tasks, moving to a custom containerized setup is the most practical fix. You can run Playwright on Fly.io to gain full control over your execution environment while stripping away the premium markups that managed scraping platforms charge. By moving your browser automation off proprietary tools and onto your own cloud infrastructure, you effectively stop paying for the "convenience tax" that often accompanies all-in-one automation suites.

Why Build Your Own Browser Infrastructure

Many founders start with managed services because they are easy to set up, but those costs become prohibitive as your usage grows. When you host Playwright on Fly.io, you shift from paying per-task or per-page to paying for the raw compute resources you actually consume. This architectural shift is a core component of long-term vibe coding cost optimization, ensuring your backend remains flexible and vendor-independent as your app matures.

Running your own browser agents offers three major advantages over closed-system alternatives:

  • Total Transparency: You see exactly how your browser sessions consume memory and CPU, allowing you to fine-tune performance.
  • Predictable Billing: You avoid unexpected usage overages common with managed scrapers that obscure their backend resource costs.
  • Infrastructure Ownership: Because you define your own Docker environment, you aren't tied to a specific provider’s runtime constraints or outdated dependency versions.

For businesses relying on frequent data extraction or automated interaction, this DIY approach provides a stable, low-cost foundation. While it requires an upfront investment in configuration, the trade-off is a leaner, more robust system that scales with your business needs rather than your monthly burn rate.

Evaluating Hosting Options for Headless Browsers

When you need to automate browser tasks, you have three primary paths. Choosing the right one depends on your team’s technical capacity and your budget structure. If you keep everything local, you face zero infrastructure costs but run into significant reliability issues; your script stops the moment your laptop closes or the office internet glitches. Managed scraping services provide a turn-key solution, but they often hide high markups in their per-request pricing, which becomes unsustainable as your automation volume scales. Before committing, it is worth comparing Apify vs Fly.io for web scraping to determine if a managed platform or a custom setup fits your needs better.

Deploying on a flexible platform like Fly.io occupies the middle ground. It grants you full control over the runtime environment while abstracting away the complex hardware management. You are essentially renting raw compute, which helps you avoid the premium fees associated with proprietary scraping platforms.

Comparing Your Hosting Approaches

  • Local Scripts: Best for development and quick tests, but inherently unreliable for production business workflows.
  • Fly.io: Offers the best cost-to-performance ratio for teams that can handle basic Docker containerization.
  • Managed APIs: Ideal if your team lacks backend knowledge and you prefer paying for convenience over infrastructure control.

Your primary cost drivers here are memory overhead and egress traffic. Browser automation is resource-intensive. A single headless instance can spike memory usage significantly, meaning you must monitor your instance sizes to ensure you aren't paying for wasted capacity while still providing enough RAM to keep the browser from crashing. Always check the current bandwidth and compute billing models on the provider’s documentation before committing to a specific region or instance size.

Hosting Options for Headless Browser Automation

Hosting TypeMaintenance EffortCost ModelScalabilityPerformance
Local/On-PremiseLowFixed/SunkNoneUnreliable
Fly.io (Self-Hosted)MediumUsage-basedHighHigh
Managed Scraping APIVery LowPremium/Per-RequestExtremeVariable

Step-by-Step: Containerizing Playwright for Deployment

To get Playwright running on Fly.io, you need a custom Dockerfile that installs the necessary Chromium dependencies. Using a base Node.js image is the most reliable path. Because Playwright requires specific system libraries to render web pages, you cannot rely on a bare-bones Alpine image without significant extra configuration.

Preparing Your Dockerfile

Start with the official Playwright Docker image or a standard Node.js image and add the following layers. This ensures the headless browser has access to shared libraries like libnss3 and libx11:

  1. Use mcr.microsoft.com/playwright:v1.x.x-jammy as your base, which comes pre-installed with browser dependencies.
  2. Set your working directory and copy your package.json and package-lock.json files.
  3. Run npm install to pull in your project requirements.
  4. Copy your application code and ensure the Playwright browser binaries are correctly located.

Common mistake: Many developers try to use alpine linux images to save space. While slim, Alpine lacks the heavy system libraries that Chromium requires to run headlessly. You will encounter cryptic "browser launch" errors and library crashes that are incredibly frustrating to debug. Stick to the Debian-based images provided by Microsoft to save yourself hours of troubleshooting.

Keep your image lean by using a .dockerignore file. Exclude your node_modules and local browser cache folders to ensure the build process remains fast. When you deploy, Fly.io builds this container and hosts it on their infrastructure, meaning your browser automation logic executes exactly as it does on your local machine. This approach balances control with deployment speed. Once your container is ready, you can follow my guide on how to build a scalable web scraping API to structure your triggers, bypassing the overhead of expensive managed browser-as-a-service platforms while maintaining full visibility into your code’s runtime behavior.

Configuring Your Fly.io App for Browser Tasks

Once your container is ready, you need to tell Fly.io how to run it. Your fly.toml file is the command center for your application. When handling browser automation, the key is balancing performance with cost by tweaking machine resources.

Critical fly.toml Settings

For Playwright, you generally need more memory than a standard API server. Focus on these specific parameters:

  • [mounts]: Define your persistent storage here. This is non-negotiable if you want to reuse browser profiles or cookies.
  • [build]: Ensure your dockerfile points to the correct build context.
  • [metrics]: Enable this to track how much memory your browser instances are consuming during peak loads.
  • [env]: Set your environment variables, specifically PLAYWRIGHT_BROWSERS_PATH, to ensure the browser binaries are located where your machine can access them reliably.

When sizing your machine, do not default to the smallest available footprint. Browser automation is memory-intensive; if your machine runs out of RAM, the browser will crash mid-task. Start with a dedicated performance-optimized machine size to handle the rendering overhead. You can always scale down later after reviewing your actual usage metrics, which helps you avoid overpaying for idle capacity.

Try this: Attach a Fly.io volume to your machine and configure Playwright to store the browser user data directory on that volume. This allows your script to stay logged into websites across different tasks, significantly reducing the load on target servers and speeding up your automation execution.

Avoid setting auto-start to true if your script runs on a schedule. Instead, use the Fly.io scheduler or an external trigger to spin up the machine only when work is pending. This is a simple way to keep your monthly overhead predictable while maintaining high performance for your scraping tasks.

Fly.io Configuration Checklist

Define Persistent StorageCritical

Attach a Fly.io volume to maintain state and cookies across runs.

Allocate Sufficient RAMCritical

Ensure your machine has at least 1GB of RAM to prevent headless browser crashes.

Configure Environment PathsHigh

Set browser binary paths so the container doesn't attempt to re-download dependencies.

Enable Resource MonitoringMedium

Track usage to optimize machine sizing and control costs.

Managing Playwright Performance and Resource Costs

Scaling Browser Instances Efficiently

When you run browser-heavy tasks, memory consumption is your biggest bottleneck. Unlike standard web services, Playwright instances can balloon in size the moment you render a page or handle complex JavaScript. On Fly.io, you should avoid running multiple browser sessions within a single machine if that machine is also managing your application's API or logic.

Instead, scale horizontally by spinning up dedicated worker machines for your automation. By decoupling your browser tasks from your main application, you ensure that a memory spike in a scraper doesn't take down your user-facing dashboard. This pattern aligns with strategies for managing background jobs, which I explain in detail in my guide on how to run background workers on Fly.io.

Monitoring and Resource Optimization

To keep costs predictable, focus on these three areas:

  • Memory Limits: Set strict memory limits in your fly.toml configuration. If a browser process leaks memory, Fly.io can automatically restart the machine, preventing runaway billing.
  • Headless Mode: Always run browsers in headless mode. It sounds obvious, but you would be surprised how many developers keep graphics rendering enabled, which wastes CPU cycles.
  • Connection Pooling: Use an existing pool of persistent browser contexts instead of launching a new browser binary for every single task. Launching a new instance is resource-intensive and adds latency.

Triggering Tasks Remotely

You don't need to manually manage these machines. You can trigger your Playwright workers from your primary application environment, such as Base44, via a simple web hook or message queue. If you are already routing work through external infrastructure, check out my notes on how to connect Base44 to Railway workers to understand how to bridge your platform-native logic with scalable, external automation workers.

Red Flags to Watch in Browser Automation Setup

When you start automating browser tasks, it's easy to get a script working on your laptop and assume it will just work forever in the cloud. In practice, production environments are far less forgiving. If you don't monitor your browser instances, you will quickly encounter stability issues that cost you time and money.

Critical Pitfalls to Avoid

One of the most common issues I see is the "zombie process" problem. If your Playwright script crashes or times out without explicitly closing the browser context and the main process, that container will hold onto system memory indefinitely. Over time, these orphaned processes stack up until your app hits its resource limit and crashes entirely.

Another major risk is IP reputation. If you run your automation from a single static IP address, sites will quickly flag your traffic as bot-driven. You must implement proxy rotation to keep your requests looking like organic traffic. Relying solely on the default outbound IP of your container host is a recipe for getting blocked during your first week of production.

Lastly, never hard-code your credentials. Storing API keys or login data inside your code is a security vulnerability that can lead to account compromise. Always use secret management features provided by your hosting platform to inject environment variables securely at runtime. Ignoring these warning signs is a common mistake that leads to broken automation pipelines.

Watch Out For These Red Flags

  • Unbounded Timeouts: Allowing scripts to run indefinitely, which leads to hanging processes and wasted compute credits.
  • Missing Error Handling: Ignoring failed page loads or navigation errors, which prevents your script from gracefully retrying.
  • Hardcoded Credentials: Storing sensitive access tokens in your source code rather than using secure platform secrets.
  • Resource Exhaustion: Allocating insufficient memory for headless browser tasks, causing sudden process termination.
Red Flags in Browser Automation
1

Zombie Processes

Orphaned browser instances consume memory until your application crashes.

2

IP Blacklisting

Running from one IP makes your bot easy to detect and block.

3

Insecure Secrets

Storing credentials in code exposes your business to data breaches.

4

Memory Leaks

Poorly managed headless sessions will exhaust container memory.

Rollout Plan: From Local Scripts to Production

Transitioning your Playwright automation from a local development environment to a production deployment requires a structured approach to avoid common pitfalls like dependency drift or resource bottlenecks. My standard process starts with containerizing the script locally to ensure the environment inside the container matches your expected production runtime.

Before pushing code to your fly.io application, I always recommend running a trial through a container image locally to catch missing system dependencies or mismatched browser binaries. Once the container builds and runs reliably on your local machine, you should deploy to a staging environment on Fly.io. This allows you to verify that environment variables, such as API keys or headless browser flags, are correctly injected into the runtime without affecting your primary live automation.

The Deployment Path

  1. Environment Consistency: Standardize your Dockerfile to include all necessary browser dependencies, ensuring the exact same versions exist in staging and production.
  2. Staging Validation: Deploy your configuration to a separate, smaller Fly.io app instance to observe memory and CPU utilization under live network conditions.
  3. Production Rollout: Once staging tests pass, point your traffic to the production instance, starting with a single worker node to monitor execution success.
  4. Continuous Monitoring: Use Fly.io’s built-in log streaming to watch for browser crashes or navigation timeouts that often don't appear during local testing.

When you are ready to scale, remember to check your resource utilization against the vendor’s current pricing page to ensure that increasing your worker count doesn't surprise you at the end of the billing cycle. By taking these small steps, you reduce the risk of broken automation tasks that could derail your business operations.

Production Deployment Timeline

1
Week 1Setup

Standardize local environment and build container image.

2
Week 2Verification

Deploy to staging environment for connectivity testing.

3
Week 3Go-Live

Finalize production deployment and verify secrets.

4
Week 4Maintenance

Establish log monitoring and resource usage analysis.

Frequently Asked Questions

Is Fly.io suitable for high-volume browser automation?

Yes, Fly.io is highly capable of running Playwright, provided you manage your machine resources and memory effectively. It is often more cost-effective than using managed scraping platforms at scale.

Do I need to manage browser dependencies manually?

When containerizing Playwright, you must include the necessary dependencies for Chromium in your Dockerfile, such as specific Linux libraries required to render web pages correctly in a headless environment.

How do I handle proxy rotation in Fly.io?

You can manage proxy rotation by passing proxy settings through environment variables in your Fly.io configuration, which your Playwright script then reads at runtime.

Can I use Fly.io volumes to save state?

Yes, mounting Fly.io volumes allows your browser instances to store cookies and cache between runs, which can improve performance and reduce the chances of being blocked by target websites.

How does Fly.io pricing compare to specialized scraping services?

Fly.io charges primarily for compute and memory consumption, whereas scraping services often charge per request or per row. For consistent, high-volume tasks, Fly.io is usually significantly cheaper.

Best Practices Summary

Optimize Dockerfile layers to keep image size small for faster deployment cycles.

Monitor individual machine resource usage in the Fly.io dashboard to fine-tune sizing.

Offload long-running tasks to background workers to keep your main application responsive.

Always implement robust error handling and browser context cleanup in your automation code.

Use environment variables to rotate proxies dynamically during execution.

Audit your automation costs monthly against the volume of tasks executed.

browser-automationplaywrightinfrastructurescaling

Want this running in your business?

We design and build custom AI agents for small businesses. Tell us about the process that's eating your week.

Book A Free Consultation →