ERPNext architecture: web, workers, scheduler, Redis and socket.io

A production ERPNext is several cooperating processes, not one web app. This page explains what each part does, how they depend on each other, and what to confirm before you size or operate one.

On this page

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.

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.

DecisionWhen it fitsTrade-off
Gunicorn worker countConcurrent interactive users are the main load; sized against measured request latency and available RAMMore 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 reportsDedicated workers isolate queues but idle capacity sits unused when one queue is quiet
Separate long queue for reports and heavy jobsMonth-end reports, large imports or scheduled exports regularly run for minutesKeeps interactive queues responsive, but adds another process to monitor and size
Scheduler on one process onlyStandard setups where periodic jobs must run once across the benchRunning it on several processes can duplicate scheduled jobs; confirm the scheduler setting for your version
Redis separation of cache and queueMultiple workers or larger sites where cache churn should not delay job deliveryTwo Redis instances add operational work; a single instance is simpler but shares failure and memory pressure
socket.io placementReal-time notifications and collaborative updates matter to users; run it as its own process behind NGINXAnother service to supervise and proxy correctly, including WebSocket upgrade headers
NGINX as single public entry pointAll browser, API and WebSocket traffic should reach one hardened listener with TLS terminationOne 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.

  1. 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 version from 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.

  2. 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.

  3. 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 doctor or the scheduler status in the System Health view, and count the scheduler processes with ps or 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.

  4. 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 doctor output 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.

  5. 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.

  6. Run a test background job

    Enqueue a harmless job and confirm a worker picks it up and completes. You can use bench execute with 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.

  7. 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.

  8. 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.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.