What runs in a production ERPNext and why it matters
A production ERPNext runs gunicorn web workers, background queue workers, a scheduler, Redis, socket.io and NGINX, all built on the Frappe Framework. Whether you choose bench on Linux, the Docker-based Easy Install, a hand-built Compose stack, Helm on Kubernetes or a managed host, you need to know these processes before you compare the options, because each one has to be installed, supervised, scaled and restarted by someone. ERPNext is licensed under GPL-3.0 and the Frappe Framework under MIT, and self-hosting is free according to the ERPNext pricing page. The cost of running ERPNext yourself is therefore the operation of these processes: their configuration, their resource use, their upgrades and their failure handling.
Before you compare deployment methods, confirm which major version you will run. According to Frappe's supported versions page, version 14 reached end of life on January 31, 2026, and version 15 and version 16 are supported until the end of 2027 and 2029. A new deployment on version 14 would start with an unsupported release, so the version decision comes before the process design. Check the Frappe supported versions page for the current status of each release, and confirm with your own support provider which version and support terms apply to your environment.
The process list also determines what you must verify on day one. Each role needs its own health check, log location and restart procedure, and Redis serves both the cache and the queue, so an outage there affects more than one function. Ask any installer or hosting provider to show you where each process runs, how its logs are collected and how its restart is handled. An answer that covers only the web tier leaves the scheduler and queue workers unmonitored, which is where background jobs such as email and scheduled tasks silently stop.
What each process does and what happens when it stops
A production ERPNext is several processes that share one database and one Redis, not a single application. The frappe_docker services split these roles into workers, scheduler, Socket.io, Redis and Nginx, so a fault in one usually shows up in a specific way for users. Check each process separately when something looks wrong.
Gunicorn web workers
The gunicorn web workers serve the ERPNext desk, the REST API and form submissions. Every browser request that loads a page or saves a document passes through them, so their failure is the first thing users notice. If they stop, users see gateway errors and pages that will not load. Check that the backend container is running, read its logs for worker timeouts, and confirm that Nginx can reach it on the expected upstream port.
Short queue workers
The short queue handles quick background jobs, such as sending notifications and small document updates that should not hold up a user's save. These jobs are time-sensitive, so a backlog here delays visible actions. If they stop, actions appear to succeed but follow-up effects are late. Check the queue worker container status and the queue length in Redis before assuming the data is lost.
Default and long queue workers
The default queue runs routine jobs, while the long queue takes heavy tasks such as large imports, report generation and bulk postings. Keeping them separate stops a long job from blocking routine work. If the long queue stops, large imports and reports sit pending while everyday tasks keep running. Check each worker's logs and the job status on the background jobs page, and confirm that the workers listen to the queues you expect.
Scheduler
The scheduler queues recurring tasks, such as periodic reminders, scheduled reports and cleanup jobs defined by ERPNext and installed apps. It enqueues work rather than running it directly, so it depends on the workers and Redis being healthy. If it stops, scheduled tasks silently do not run. Check the scheduler container and confirm that its last run time is recent, because the failure produces no error on the user screen.
Socket.io real-time service
The Socket.io service pushes live updates to open browser sessions, such as notifications, document change alerts and progress messages from background jobs. It runs as its own process and connects through Redis. If it stops, pages still work but live updates and progress bars stop refreshing. Users may think jobs are stuck, so check the Socket.io container and its connection to Redis before restarting workers.
Redis cache and queue
Redis holds the cache that speeds up page loads and the queues that the workers and scheduler depend on. Because two roles share the same service in many setups, a Redis outage affects almost every other process at once. If Redis stops, slow pages, stalled jobs and lost live updates appear together. Check memory use, persistence settings and whether the cache and queue are separated, and confirm the connection settings in the site configuration match the running instance.
How the same processes map to bench, Docker Compose and Kubernetes
With a bench install, every process runs as a service on a host you manage. The bench command line tool creates the bench directory and the sites, and production mode hands the long-running processes to a process supervisor and public traffic to NGINX. Your team decides how each process is started, restarted, logged and monitored.
- Gunicorn web workers: These serve the Python application behind NGINX. Supervisor keeps them running, and your team sets their count and timeouts in the bench configuration.
- Background workers: Short, default and long queue workers each run as a supervised process. Concurrency and restart behavior are set by your team.
- Scheduler: The scheduler runs periodic jobs and must be enabled for each site. Confirm the exact commands for your bench version before you rely on them.
- Socket.io and Redis: The socket.io process and the Redis cache and queue instances are separate services on the host. Installing, securing and persisting Redis is a host task.
- NGINX and TLS: NGINX routes requests and serves static assets. Certificates, operating system patching and backups all remain with your team.
After each reboot, confirm that every process appears in the supervisor status output and that a stopped worker triggers an alert.
The frappe_docker repository defines ERPNext as a set of containers, with services for MariaDB, Redis, background workers, the scheduler, Socket.io and NGINX. Docker starts and restarts these containers according to the Compose file, so the process layout is visible in one place. Your team chooses the images, volumes, environment values and whether the database and Redis run as containers or as external services.
- One container per process: The web, worker, scheduler, Socket.io and NGINX services can be restarted or scaled separately.
- Shared sites volume: Every container that reads site files must mount the same sites directory. Confirm the volume layout in the Compose files you use.
- Database and Redis: Compose can run MariaDB and Redis as containers or point to managed services. Backups, persistence and upgrades stay with your team in both cases.
- Image and tag pinning: Your team pins the ERPNext image and its apps, and decides when containers are recreated.
- Demo versus production: The single-file demo setup is for short-lived evaluation only. Any environment with real data needs a hand-built Compose setup with your own volumes, secrets and backup routine.
Before go-live, confirm that a container restart does not lose site files, and that worker and scheduler containers come back in the expected order.
The frappe/helm chart maps the same roles to Kubernetes objects. Web, Socket.io, each worker queue and the scheduler run as Deployments, and one-off tasks such as site creation and migrations run as Jobs. Kubernetes handles scheduling, restarts and replica counts, but your team supplies the storage, data services, ingress and sizing that the chart depends on.
- Deployments per role: Each role is a separate Deployment, so worker replicas for each queue can be scaled independently.
- Jobs for one-off tasks: Site creation and migrations run as Jobs rather than inside long-running pods. Confirm how the chart version you use orders these Jobs.
- Shared storage: Pods that read site files need ReadWriteMany storage. The storage class and its performance are your decision.
- External data services: MariaDB and Redis are typically external to the chart. High availability, failover and backups for them are your responsibility.
- Ingress, TLS and probes: Ingress, certificates and health checks are set in the chart values. Confirm the probe settings for each Deployment before you scale it.
Before you scale worker replicas, confirm how the chart handles the scheduler, since running more than one scheduler instance is a common mistake to check for.
Frappe Cloud is the hosted option for ERPNext. The same web, worker, scheduler and Redis layers run on Frappe's platform, so your team does not operate supervisors, containers or Redis hosts itself. The trade-off is less control over the server and the configuration. The ERPNext pricing page lists the current hosting options.
- Hosted processes: Frappe hosts the process model, so no supervisor, Compose or Helm setup is needed on your side.
- Starting prices: According to the ERPNext pricing page, shared site hosting starts at 5 US dollars per month, shared servers at 20 US dollars per month and dedicated servers at 125 US dollars per month. Confirm current terms before budgeting.
- Script limits: Public shared benches do not allow Server Scripts, which affects custom logic that depends on them.
- Support coverage: Warranty coverage from Frappe depends on running a listed version with a matching Frappe Cloud support plan.
- Remaining decisions: Custom apps, access controls, data location and backup requirements still need review against your own obligations.
Before you choose this option, compare the hosting limits with the customizations your plan requires.
Which process settings to decide before sizing
This table compares the process-level choices that shape a production ERPNext: web workers, queue workers, the scheduler, Redis, socket.io and NGINX. Read each row for when the option fits and what it costs in complexity, memory or failure isolation. Confirm default values in the exact ERPNext version you run before you size anything.
| Decision | When it fits | Trade-off |
|---|---|---|
| Gunicorn worker count | Concurrent interactive users are the main load; sized against measured request latency and available RAM | More workers serve more simultaneous page requests but each consumes memory and database connections |
| Workers per queue (short, default, long) | Mixed load with fast tasks such as emails and long tasks such as bulk postings or reports | Dedicated workers isolate queues but idle capacity sits unused when one queue is quiet |
| Separate long queue for reports and heavy jobs | Month-end reports, large imports or scheduled exports regularly run for minutes | Keeps interactive queues responsive, but adds another process to monitor and size |
| Scheduler on one process only | Standard setups where periodic jobs must run once across the bench | Running it on several processes can duplicate scheduled jobs; confirm the scheduler setting for your version |
| Redis separation of cache and queue | Multiple workers or larger sites where cache churn should not delay job delivery | Two Redis instances add operational work; a single instance is simpler but shares failure and memory pressure |
| socket.io placement | Real-time notifications and collaborative updates matter to users; run it as its own process behind NGINX | Another service to supervise and proxy correctly, including WebSocket upgrade headers |
| NGINX as single public entry point | All browser, API and WebSocket traffic should reach one hardened listener with TLS termination | One point to configure and certify, and a misconfigured proxy affects every service behind it |
How to verify the process layout after installation
This sequence confirms that each ERPNext process type is running, is wired to the right queues and proxy, and completes real work, so you can tell a healthy layout from one that only looks installed. Run it on a staging copy before the first production release.
Confirm the ERPNext and Frappe versions
Start by checking the installed ERPNext and Frappe Framework versions against the supported versions page before you test anything else. Run
bench versionfrom the bench directory, or read the image tags in your Compose file if you use frappe_docker. Confirm both products are on a listed major version and that the minor release is one you intend to keep. The step is done when the versions match your change record and the major version is still supported.Start and inspect each process type
Bring up every process type and confirm each one is alive. On a bench-managed server, check the processes that Supervisor controls, including the web workers, the scheduler, socketio and the worker groups for the short, default and long queues. In frappe_docker, check the backend, scheduler, socketio, queue-short, queue-default, queue-long, redis and nginx services with
docker compose ps. The step is done when every service shows running and none is restarting in a loop, with logs showing no startup errors.Confirm the scheduler runs once
Confirm the scheduler is active and that only one instance is running. The scheduler should be a single process per site group, so a duplicate would fire scheduled jobs twice. Use
bench doctoror the scheduler status in the System Health view, and count the scheduler processes withpsor the Compose service scale. Also check that scheduled events are enabled for the site. The step is done when exactly one scheduler is active and the status reports it as enabled.Verify workers consume the expected queues
Check that each worker group is subscribed to the queue it was meant to serve. Short jobs should sit on the short queue, routine work on default, and heavy jobs such as large reports or bulk imports on long. Use the RQ worker view in the Frappe desk or
bench doctoroutput to list workers and their queues. Compare that list to your Supervisor or Compose configuration. The step is done when every queue has at least one worker and no worker is attached to a queue you did not configure.Confirm socket.io connects through NGINX
Test the real-time channel through the same proxy your users will use. Log in through the public URL, open a document, and watch the browser developer tools for a WebSocket connection to the socket.io path. In frappe_docker, the Nginx service must forward that path to the socketio service. The step is done when the connection stays open and the status indicator in the desk shows the live connection, not a fallback or reconnect loop. If it fails, check the proxy configuration before changing the application.
Run a test background job
Enqueue a harmless job and confirm a worker picks it up and completes. You can use
bench executewith a short, existing Frappe function, or trigger a background action such as an email queue flush from the desk. Then check the RQ Job list or the worker logs for the job's status moving from queued to finished. Note the queue name the job used. The step is done when the job finishes on the expected worker and the result is visible in the application.Confirm a scheduled event fires
Verify that the scheduler triggers a scheduled event on time. Choose a scheduled job that is safe to run in staging, or create a temporary Scheduled Job Type in a test site with a short interval, and then remove it. Check the Scheduled Job Log for a successful entry with a timestamp near the expected run. The step is done when the log records a completed run and no errors, and the test job is removed afterward so it does not run in production.
Record the verified layout as a baseline
Save the results of each check in a short runbook entry with the versions, service list, queue mapping, proxy test and job logs. Store it with your change record so the next upgrade, scaling change or restore has a known-good reference. Ask your partner or internal team to confirm that the test site and any temporary scheduled jobs were removed. The step is done when the baseline is filed and someone other than the installer has reviewed it.
Why the scheduler and queue workers need careful separation
The scheduler does not run jobs itself. It enqueues them, and workers execute them, so a stalled or overloaded queue delays both the background work users trigger from the interface and the scheduled tasks that run on a timer. In a production ERPNext, the scheduler process and the worker processes for the short, default and long queues should be sized and watched as one chain. The practical check is to confirm, before go-live, which worker serves each queue, how many workers consume it, and what happens to scheduled jobs when that pool is busy. Confirm these details against the Frappe version you run, since queue configuration and scheduler behavior are documented in the Frappe Framework documentation rather than in this page.
Running more than one scheduler instance is a risk you must design out. Two schedulers enqueue the same periodic jobs, which can duplicate emails, ledger postings or stock updates depending on what each job does. Confirm in your deployment that exactly one scheduler process is active, whether you run bench on a single server or a container platform where replicas are easy to add by accident. Keep long jobs such as large imports or heavy reports away from the short queue. A single long task on a worker that also serves interactive work can hold up users and scheduled tasks alike. The Data Import documentation advises importing only a few thousand records at a time, so plan large loads as several batches, with their timing set outside business hours, rather than as one background job.
Security and operational considerations for the process layout
A production ERPNext is several processes that share Redis, a database and one public entry point. The cards below cover where that exposure sits, which behaviors change by hosting model, and how to see and recover each process when something fails.
Keep Redis on the private network
Redis holds both the cache and the background job queues, so anyone who reaches it can read or disturb work in flight. Bind it to the private network only, do not publish its port on a public interface, and confirm that the Redis instance used by workers, the scheduler and socket.io requires authentication in your build. Check the Redis settings in your Compose file, Helm values or bench configuration rather than assuming a default. Ask your team to test that a connection from outside the application network is refused.
NGINX as the only public entry
In a production layout, NGINX receives client traffic, terminates TLS and forwards requests to gunicorn and to the socket.io process. Gunicorn, workers, the scheduler and Redis should not accept connections from the internet. Verify this by scanning the host from outside your network and by listing which ports each container or service publishes. Decide where certificates are stored, who renews them and how the renewal is tested, since a failed renewal takes down every process behind NGINX at once.
Socket.io through the same origin
Real-time updates in the ERPNext desk use socket.io, which runs as its own process. Its browser connection must reach the same public host name as the web application, so NGINX routes that path to the socket.io service. A mismatch in host name, a missing WebSocket upgrade header or a blocked path shows up as pages that load but never refresh. Confirm the proxy configuration for your host name and check the browser console after a fresh login. Do not open the socket.io port directly to users as a workaround.
Server Scripts depend on the host
Frappe Framework version 15 and later disables Server Scripts by default. They must be enabled with the server_script_enabled setting, and Frappe Cloud public shared benches do not allow them at all. A customization that depends on a Server Script will therefore behave differently on a self-hosted bench, a Docker deployment and a shared cloud bench. Record which hosting model each script depends on, and test queue-based and scheduled logic on the same host type that runs production.
Per-process logs and error visibility
Each role writes its own logs: gunicorn requests, worker job failures, scheduler activity, socket.io connections and NGINX access. A failed invoice posting may appear only in a worker log, while the user sees a generic error in the browser. Decide where each process writes, how long logs are kept, and who reviews failed background jobs. Ask your implementation partner to show the commands or dashboards your team will use to find a failed job and the document it belongs to.
Restart behavior after a crash
Each process should restart on its own after a crash, and the scheduler and worker processes need to resume without duplicating or losing queued jobs. Confirm how your supervisor, container restart policy or Kubernetes deployment handles repeated crashes, and whether a crash loop raises an alert. Test a planned restart of the worker and scheduler during a quiet period, then check that queued jobs complete once and that no scheduled job runs twice.
Questions buyers ask about ERPNext architecture before hiring help
Can a single server run every ERPNext process?
Yes, a single server can run the web workers, queue workers, scheduler, socket.io, Redis and NGINX together, and many small installations do exactly that. The catch is that the processes compete for the same CPU, memory and disk, so a long-running report or a bulk import can slow down logins and document saves for everyone. Separating the database onto its own host and giving long queue jobs their own worker capacity are the usual first steps. Confirm the exact process layout and resource needs against the version you choose and your data volume before you commit to a server plan.
What makes running ERPNext architecture hard to operate day to day?
The effort comes from keeping several long-lived services healthy at once: gunicorn workers serving requests, short, default and long queue workers draining background jobs, the scheduler queuing periodic tasks, socket.io pushing live updates, and Redis holding both cache and queue data. A failure in one piece often looks like a symptom somewhere else, such as stalled emails or reports that never finish. Effort rises with the number of sites, custom apps that add background jobs, and the volume of scheduled work. Ask any partner to define how they will monitor each process and where logs are kept.
Can our own team run ERPNext without a partner?
A team with Linux administration, database and Python experience can run ERPNext on its own, and the open-source project and its documentation cover the core setup. The questions that decide the answer are whether you have someone who can own upgrades, respond to queue or database incidents out of hours, and review logs regularly. Teams that lack these skills usually bring in a partner for the first build and then keep a support arrangement. Be clear internally about who owns each of these tasks after go-live.
How long does an ERPNext architecture setup take?
Duration depends on scope: the number of sites, whether you need a separate database server, how many custom apps and integrations are involved, and how much of the environment already exists. Ask for a timeline in the written scope, with each milestone, the environments covered and the acceptance tests listed. Do not accept a duration that is not tied to a defined scope, because the same architecture can take very different effort depending on what sits around it.
What must our team provide before the work starts?
Most of the dependency sits with you. Expect to provide access to your cloud accounts, data centers or private network, the server and DNS details, the ERPNext and Frappe version you have agreed to run, and the data sources that feed or consume the system. You also need to confirm who approves changes to production and where backups will be stored. Missing any of these items usually moves the schedule, so list them in the scope before signing and assign an owner to each one.
What recurring work does a self-hosted ERPNext require?
Recurring work includes applying minor and major version upgrades, watching queue depth and failed background jobs, reviewing web and worker logs, checking scheduler health, managing backups and restore tests, and renewing certificates. Upgrades are the largest item, because the supported versions have fixed end-of-life dates and each major step can change how apps behave. Budget for this as an ongoing operations task, whether it is done in-house or under a support retainer, and agree the response expectations in writing.
How does Frappe Cloud compare with self-hosting on cost?
Using only the published pricing, self-hosting ERPNext is free as software, while Frappe Cloud shared site hosting starts at 5 US dollars per month, Frappe Cloud servers start at 20 US dollars per month for shared instances, and dedicated servers start at 125 US dollars per month, according to the ERPNext pricing page. Self-hosting shifts those amounts into your own infrastructure, staff time and support, so the real comparison is your fully loaded operations cost against the hosting fee. Ask any partner for a quote that separates one-time setup from recurring operations and infrastructure.
Official sources for ERPNext process design
Before you size, deploy or customize a process tier, check the behavior against the source code and documentation for your exact release. The frappe/erpnext repository is the place to confirm which worker queues, scheduler hooks and socket.io handlers your version ships, since the process layout is tied to the Frappe Framework version you run. Read the frappe/erpnext repository alongside the release notes for the minor version you plan to install, because queue names and default job assignments can change between releases. Treat the repository as the authority for ERPNext behavior, and treat a blog post or forum answer as a lead to verify rather than a specification.
Two other sources answer the questions that matter most for process design. The frappe_docker repository shows how the official images separate gunicorn, workers, the scheduler, Socket.io, Redis and Nginx into distinct services, which is a useful reference when you decide how to split them across hosts. Check the supported versions page before you commit to a release, because the end-of-life dates determine how long a given process layout will receive fixes. Finally, if your design depends on running Python in response to document events, read the Server Script documentation first: it states that these scripts are disabled by default from version 15 onward and must be enabled with server_script_enabled, which changes where custom logic can run in your architecture.

