When deploying web applications on your own infrastructure, choosing the right application server is critical. The application server sits between your code and the network, handling HTTP requests, managing connections, and executing your application logic. Three of the most widely used open-source options are Apache Tomcat, WildFly, and Gunicorn — each serving different ecosystems and use cases.
This guide compares these three application servers across performance, features, ease of deployment, and resource consumption to help you pick the right one for your self-hosted environment.
For related reading, see our Nginx vs Caddy vs Traefik web server comparison and Nginx Proxy Manager vs SWAG vs Caddy reverse proxy guide for understanding how to route traffic to your application server.
Why Self-Host Your Application Server?
Running your own application server gives you full control over your deployment stack:
- No vendor lock-in — you own the infrastructure and can migrate at will
- Data sovereignty — application logs, session data, and metrics stay on your servers
- Cost predictability — fixed infrastructure costs instead of per-request pricing
- Custom configurations — tune thread pools, connection limits, and JVM/interpreter settings to your workload
- Network isolation — deploy behind your own reverse proxy, WAF, and load balancer
Whether you are serving Java servlets, Jakarta EE applications, or Python web frameworks, having a dedicated application server that you fully control is a foundational building block of self-hosted infrastructure.
Apache Tomcat
Apache Tomcat is the most widely deployed Java application server in the world. It implements the Jakarta Servlet, Jakarta Server Pages (JSP), and WebSocket specifications. As of April 2026, the apache/tomcat repository has over 8,100 stars and is actively maintained.
Tomcat is designed to be lightweight and focused. Unlike full Java EE servers, it does not implement Enterprise JavaBeans (EJB) or JMS — it specializes in serving web applications (WAR files) efficiently.
Key Features
- Implements Jakarta Servlet 6.0, JSP 3.1, WebSocket 2.1, and Expression Language 5.0
- Embedded mode available for Spring Boot and other frameworks
- JMX management and monitoring
- Valve-based request processing pipeline
- Realm-based authentication (JDBC, LDAP, JAAS, memory)
- Hot-reload of WAR files without server restart
Installation
Package manager (Debian/Ubuntu):
| |
Docker Compose:
| |
Docker with manager access:
| |
Performance Characteristics
Tomcat uses a thread-pool model with configurable connector settings. For production deployments:
| |
Typical memory footprint: 256-512 MB heap for moderate workloads. Handles 5,000-15,000 requests per second depending on application complexity and hardware.
WildFly (Formerly JBoss AS)
WildFly is a full-featured Jakarta EE application server maintained by Red Hat. With over 3,100 GitHub stars on wildfly/wildfly, it is the open-source upstream for Red Hat JBoss Enterprise Application Platform.
Unlike Tomcat, WildFly implements the complete Jakarta EE specification, including EJB, JMS, JTA, CDI, JPA, and more. It is the right choice when you need enterprise-grade features beyond servlets.
Key Features
- Full Jakarta EE 10 certification
- Elytron security subsystem (unified security framework)
- Undertow HTTP server (high-performance, non-blocking)
- Domain mode for managing multiple server instances from a central controller
- Hot deployment and rollback
- Built-in clustering and session replication
- Management CLI and web console
- MicroProfile support for cloud-native Java
Installation
Package manager (RHEL/Fedora):
| |
Docker Compose:
| |
Docker with management console:
| |
Performance Characteristics
WildFly’s Undertow HTTP server delivers excellent performance for a full EE server. The management overhead (domain mode, clustering) adds to the baseline resource usage.
Typical memory footprint: 512 MB - 1 GB heap minimum. Throughput of 3,000-10,000 requests per second depending on EE features used. Domain mode adds ~200 MB overhead per managed server instance.
Gunicorn
Gunicorn (Green Unicorn) is a Python WSGI HTTP server. With over 10,500 stars on benoitc/gunicorn, it is the most popular production-grade Python application server.
Unlike Tomcat and WildFly, Gunicorn is not a Java server — it serves Python web applications built on frameworks like Django, Flask, FastAPI, and Pyramid. It uses a pre-fork worker model and is designed for UNIX environments.
Key Features
- Pre-fork worker model with multiple worker types (sync, gevent, eventlet, uvicorn workers)
- Graceful reload and hot code upgrades without dropping connections
- Automatic worker crash recovery
- Configurable worker timeout and keepalive
- Integration with systemd socket activation
- Low memory footprint compared to JVM-based servers
- Native support for WSGI applications
Installation
pip:
| |
Docker Compose with Django:
| |
Docker Compose with FastAPI:
| |
Production Gunicorn configuration file (gunicorn.conf.py):
| |
Performance Characteristics
Gunicorn’s pre-fork model is lightweight and efficient. Memory per worker varies by framework — a Django worker typically uses 80-150 MB, while a minimal Flask worker uses 30-50 MB.
Throughput: 1,000-5,000 requests per second per worker with sync mode. With async workers (uvicorn/gevent), this can reach 10,000-20,000+ rps for I/O-bound workloads.
Comparison Table
| Feature | Apache Tomcat 10 | WildFly 33 | Gunicorn 22 |
|---|---|---|---|
| Language | Java | Java | Python |
| Specification | Jakarta Servlet, JSP, WebSocket | Full Jakarta EE 10 | WSGI (PEP 3333) |
| GitHub Stars | 8,154 | 3,160 | 10,556 |
| Memory Footprint | 256-512 MB | 512 MB - 1 GB | 30-150 MB per worker |
| Throughput (rps) | 5,000-15,000 | 3,000-10,000 | 1,000-20,000+ |
| Hot Deployment | Yes (WAR) | Yes (EAR/WAR/JAR) | Yes (via graceful reload) |
| Management UI | Tomcat Manager | WildFly Admin Console | None (CLI only) |
| Clustering | Limited (session replication) | Full (domain mode) | Via external load balancer |
| Embedded Mode | Yes | Yes (WildFly Swarm) | Yes (as library) |
| Security | Realms, JAAS | Elytron subsystem | Basic auth, SSL |
| JMX Monitoring | Yes | Yes | No (external tools needed) |
| Docker Image Size | ~350 MB | ~600 MB | ~150 MB (slim Python) |
| Best For | Java web apps, Spring Boot | Enterprise Java, Jakarta EE | Django, Flask, FastAPI |
When to Choose Each Server
Choose Apache Tomcat When:
- You are deploying Java web applications (WAR files)
- You need Servlet/JSP support without full Jakarta EE overhead
- You want a battle-tested server with decades of production use
- You are using Spring Boot’s embedded Tomcat and want to externalize it
- You need JMX-based monitoring and management
Choose WildFly When:
- Your application uses EJB, JMS, JTA, or other Jakarta EE components
- You need domain mode to manage multiple server instances centrally
- You require built-in clustering, session replication, and load balancing
- You are migrating from JBoss EAP and want the open-source equivalent
- You need MicroProfile support alongside full Jakarta EE
Choose Gunicorn When:
- You are deploying Python web applications (Django, Flask, FastAPI)
- You need a lightweight server with minimal memory overhead
- You want to use async workers for high-concurrency I/O-bound workloads
- You are containerizing Python apps and want small Docker images
- You prefer simplicity over feature richness
Production Architecture Patterns
Pattern 1: Tomcat Behind Nginx
| |
| |
Pattern 2: WildFly Domain Mode
| |
Start domain controller:
| |
Start managed servers:
| |
Pattern 3: Gunicorn with Systemd
Create /etc/systemd/system/gunicorn.service:
| |
| |
Security Hardening
Tomcat Security
| |
WildFly Security
| |
Gunicorn Security
| |
FAQ
Can Tomcat run Python applications?
No. Apache Tomcat is a Java-based server that implements the Jakarta Servlet specification. It can only run Java web applications packaged as WAR files. For Python applications, use Gunicorn or another WSGI/ASGI server.
Is WildFly compatible with Spring Boot?
Yes, but with caveats. WildFly can deploy Spring Boot applications, but Spring Boot is designed primarily for embedded servers (Tomcat, Jetty, Undertow). For WildFly, you should package as a WAR and exclude the embedded server from Spring Boot’s dependencies.
Can Gunicorn handle WebSocket connections?
Not natively. Gunicorn is a WSGI server and the WSGI specification does not support WebSocket. For WebSocket in Python, use an ASGI server like Uvicorn or Daphne. You can use Gunicorn with Uvicorn workers (-k uvicorn.workers.UvicornWorker) for ASGI applications like FastAPI that include WebSocket support.
Which application server uses the least memory?
Gunicorn is the lightest by a significant margin. A single Gunicorn worker running a Flask app uses approximately 30-50 MB of RAM, while Tomcat needs at least 256 MB and WildFly 512 MB minimum due to JVM overhead and EE subsystem initialization.
Can I run multiple applications on a single server instance?
Tomcat supports multiple web applications via separate WAR files in the webapps/ directory. WildFly can deploy multiple EAR/WAR files simultaneously with isolation between deployments. Gunicorn runs one Python application per instance — for multiple Python apps, run separate Gunicorn processes behind a reverse proxy.
How do I monitor these servers in production?
Tomcat provides JMX metrics accessible via JConsole or VisualVM. WildFly offers a comprehensive management console at port 9990 and CLI access. Gunicorn has no built-in monitoring — use external tools like Prometheus with the gunicorn-exporter or integrate with your logging stack (ELK, Loki). For all three, reverse proxy access logs provide request-level visibility.
Do these servers support automatic SSL/TLS termination?
None of them handle SSL/TLS termination natively in recommended production setups. The standard pattern is to place a reverse proxy (Nginx, Caddy, or Traefik) in front of the application server to handle TLS termination, HTTP/2, and load balancing. Caddy can auto-provision Let’s Encrypt certificates, making it the simplest option for SSL management.
Can these servers run inside Docker containers?
Yes, all three have official Docker images. Tomcat and WildFly provide images with pre-configured JVM settings. Gunicorn is typically run in a custom Docker image that bundles your Python application. For production, always set resource limits (--memory, --cpus) on Docker containers and configure health checks.