Emma Larsson
VPS Technical LeadEmma Larsson is a lead systems developer and virtualization specialist with a decade of expertise in kernel configurations and hypervisor scaling.
Deploying a Python Django application is not the same as uploading a static HTML site or installing WordPress. Django relies on a WSGI or ASGI application server, a database backend, a reverse proxy, and often a task queue for background jobs. Shared hosting cannot meet those requirements because it restricts long-running processes, custom ports, and system-level packages. A virtual private server (VPS) gives you the root access and resource isolation that Django needs to run reliably, and selecting the right VPS provider can make the difference between a sluggish admin panel and a production-ready deployment.
At HostingCaptain, we have tested Django deployments on dozens of VPS configurations, from budget-friendly cloud instances to bare-metal virtualized servers with NVMe storage. This guide draws on that hands-on experience to help you choose a vps for django app that balances PostgreSQL performance, static file delivery, and cost without over-provisioning resources your application will never use.
Django applications are built around the WSGI specification (or ASGI for async workloads), which requires a persistent server process such as Gunicorn, uWSGI, or Daphne. Shared hosting environments terminate PHP processes after each request, but a Python application server must stay resident in memory. Most shared hosting providers also restrict the installation of system packages like libpq-dev for PostgreSQL or python3-dev for compiling C extensions. Even when a shared plan advertises Python support, the environment is usually limited to a single version of Python in a jailed shell, which makes it impossible to isolate dependencies with virtual environments.
A VPS, defined as a virtualized slice of a physical server with its own kernel-visible resources as explained by Wikipedia, gives you full control over the operating system. You can install any Python version, compile database drivers, configure a reverse proxy like Nginx, and run background workers with Celery. More importantly, VPS resources such as CPU cores, RAM, and disk I/O are guaranteed rather than shared. A Django application handling database queries, template rendering, and API responses all at once will saturate a shared CPU quickly, and the resulting request queuing is invisible to the user but deadly for conversion rates. Our guide to VPS hosting explains the underlying technology in greater detail.
Consider the typical Django stack: Nginx receives requests and proxies them to Gunicorn, which communicates with Django through WSGI. Django then queries PostgreSQL or MySQL, and the response travels back up the chain. This pipeline involves at least three long-lived processes that need independent CPU time and memory allocation. Shared hosting cannot guarantee any of that, which is why the official Django deployment documentation assumes a VPS or dedicated environment from the very first paragraph.
A production Django deployment usually demands the following components, all of which a VPS can host without restriction:
Add those up and a modest Django application with two Gunicorn workers, PostgreSQL, Redis, Nginx, and one Celery worker can easily consume 1 GB of RAM before serving a single user request. That is well beyond what any shared hosting plan offers. If you are curious about the lower end of the VPS market, our analysis of one-dollar VPS plans explores whether ultra-budget options are viable.
Sizing a VPS for Django is about balancing three resources: CPU cores, RAM, and storage speed. The most common mistake we observe at HostingCaptain is over-provisioning CPU while under-provisioning RAM and storage I/O. Django's ORM generates complex SQL queries that benefit from fast random-read performance, which means NVMe storage is often more impactful than an extra CPU core.
Django applications are memory-hungry because each Gunicorn worker loads the entire Python codebase into its own address space. If your Django project has a large number of installed apps and third-party packages, the base memory footprint per worker grows. PostgreSQL also thrives on RAM because it uses shared buffers to cache frequently accessed tables and indexes. A rule of thumb we use at HostingCaptain: allocate 512 MB for the operating system and essential services, 256 MB for PostgreSQL, 128 MB for Redis, and 150 MB per Gunicorn worker. A small blog with two workers therefore needs about 1.2 GB, while a mid-sized SaaS application with four workers needs 1.6 GB minimum. Headroom matters too—running at 90% memory utilization invites the OOM killer to terminate PostgreSQL at the worst possible moment.
Python is single-threaded by default due to the Global Interpreter Lock (GIL), which means a single Gunicorn worker can use only one CPU core at a time. Multiple workers allow Django to handle concurrent requests across different cores. For a standard Django application, 2–4 vCPU cores are sufficient for handling dozens of concurrent requests, assuming your database queries are well-indexed and you use Django's select_related and prefetch_related to avoid N+1 query patterns. CPU clock speed matters more for template rendering and complex business logic, so providers that offer high-frequency cores (3.0 GHz and above) will yield faster Time to First Byte than lower-clocked alternatives.
Database performance is bound by disk I/O more than by CPU. PostgreSQL write-ahead logs are written sequentially, but random reads during query execution require low latency. NVMe drives deliver 3–5× the IOPS of SATA SSDs, which translates directly into faster admin panel page loads when your database grows beyond a few hundred thousand rows. For a production Django application, we recommend a minimum of 25 GB of NVMe storage. Media file uploads, database snapshots, and log rotation will consume space over time, so 50 GB is a safer baseline. Providers like DigitalOcean, which we examine in our DigitalOcean Droplets guide, offer NVMe-backed volumes as a standard feature on entry-level plans.
Having the right hardware is one half of the equation. Configuring the software stack correctly for Django determines whether your application survives a traffic spike or collapses under moderate load. At HostingCaptain, we follow a consistent deployment pattern that has proven reliable across hundreds of client projects.
Ubuntu Server LTS (currently 24.04) is the most widely documented distribution for Django deployments. Its package repositories include up-to-date versions of PostgreSQL, Nginx, and Redis, and its long-term support cycle aligns with production server expectations. Always install Python from the deadsnakes PPA or compile from source rather than relying on the distribution package, which may be several minor versions behind. At the time of writing, Python 3.12 is the recommended target for new Django 5.x projects. Use pyenv or python3.12-venv to create an isolated virtual environment so that system Python packages do not interfere with your application dependencies.
Gunicorn should be configured with the --workers flag set to (2 × CPU cores) + 1 as a starting point, though for RAM-constrained VPS instances you may reduce the multiplier to one worker per core. Each worker should be bound to a Unix socket rather than a TCP port so that Nginx communicates with Gunicorn without network stack overhead. Nginx, in turn, serves static files from the /static/ and /media/ directories directly, bypassing Django entirely for those requests. The proxy_pass directive in Nginx forwards everything else to the Gunicorn socket.
A production Nginx configuration for Django should also set appropriate buffer sizes for client request bodies to prevent slow clients from holding worker connections open, and should include X-Forwarded-For and X-Forwarded-Proto headers so that Django's SECURE_PROXY_SSL_HEADER and IP logging work correctly behind the proxy.
Django's ORM is optimized for PostgreSQL, and while MySQL and MariaDB are supported, PostgreSQL offers superior JSON field support, full-text search capabilities, and a more robust migration system. On a VPS with limited RAM, you should adjust PostgreSQL's shared_buffers to approximately 25% of available system memory, effective_cache_size to 50%, and work_mem to a conservative 8–16 MB to prevent each query connection from consuming excessive memory. These settings are not one-size-fits-all; they should be tuned based on your query patterns and data volume, but they provide a safe starting point for a Django application on a 2 GB VPS.
Not all VPS providers are equally suited to Django. Some excel at raw CPU performance, others at network throughput or global data center presence. The following comparison reflects real-world testing performed by the HostingCaptain editorial team using a standardized Django application with 15 models, 12 views, and a PostgreSQL database seeded with 50,000 rows.
DigitalOcean Droplets offer NVMe storage by default on plans starting at $6 per month for 512 MB RAM and 1 vCPU. The one-click Django droplet saves 15–20 minutes of manual setup, but we recommend provisioning a clean Ubuntu droplet and configuring the stack manually for full control over Python versions and package sources. DigitalOcean's control panel includes integrated firewall rules, snapshot backups for $0.05 per GB per month, and a global CDN for static assets via Spaces. Droplet CPU performance is consistent, with negligible noisy-neighbor effects in our multi-month monitoring period.
Linode's shared CPU plans start at $5 per month for 1 GB RAM, which is more generous at the entry level than DigitalOcean. Their dedicated CPU plans, starting at $36 per month for 4 GB RAM and 2 dedicated cores, eliminate CPU steal time entirely—important for Django applications that must meet latency SLAs. Linode's API and CLI tools are well-documented, making it easy to automate environment provisioning with Terraform or Ansible, a workflow that HostingCaptain recommends for agencies managing multiple client deployments.
Vultr's High Frequency plans use 3.8 GHz NVMe-backed instances that outperform similarly priced alternatives on CPU-bound tasks such as Django template rendering and database query parsing. Their $6 per month plan includes 1 GB RAM and 25 GB NVMe, competitive with Linode. Vultr offers 32 data center locations, more than any other provider in this comparison, which is useful if your Django application serves a globally distributed user base and you want to minimize latency to the primary database server.
Hetzner's CX line provides the best price-to-performance ratio among European providers. The CX22 plan at approximately €3.99 per month includes 2 GB RAM, 2 vCPU, and 40 GB NVMe storage—specifications that comfortably host a medium Django application. Hetzner's network throughput is generous at 20 TB per month of included traffic. The trade-off is a smaller geographic footprint (data centers in Germany, Finland, and the United States only) and a less polished control panel UI. For European audiences, however, Hetzner is often unbeatable on value.
Running your own VPS means you are responsible for security configurations that a managed hosting provider would handle for you. Django includes several built-in security features, but they must be explicitly enabled and correctly configured to protect your application and its data.
Start by disabling password-based SSH authentication and enforcing key-only access. Configure ufw (Uncomplicated Firewall) to allow only ports 22 (SSH), 80 (HTTP), and 443 (HTTPS). Install fail2ban to block IP addresses that repeatedly fail authentication attempts. Keep the operating system updated with automatic security patches applied through unattended-upgrades. These basic hardening steps prevent the vast majority of automated attacks that target VPS instances within minutes of being provisioned.
Django's SECURE_SSL_REDIRECT, SECURE_HSTS_SECONDS, SESSION_COOKIE_SECURE, and CSRF_COOKIE_SECURE settings should all be enabled in production. Set SECURE_HSTS_INCLUDE_SUBDOMAINS to True and SECURE_HSTS_PRELOAD after confirming that all subdomains are accessible over HTTPS. The X_FRAME_OPTIONS setting should be set to DENY unless you have a legitimate need to embed your application in frames. Django's SECRET_KEY must be a cryptographically random string stored in environment variables rather than committed to version control.
PostgreSQL should be configured to accept connections only from localhost unless you have a separate database server. Use strong passwords for the database user and limit its privileges to the specific database your Django application accesses. Automated backups should be encrypted at rest and stored off-server—many VPS providers offer automated snapshot backups, but you should also implement pg_dump to an external S3-compatible bucket as a secondary backup layer. If a VPS provider suffers a catastrophic failure, snapshots stored in the same data center may be unrecoverable, which is why an off-site backup strategy is essential.
Containerizing your Django application with Docker adds a layer of environment consistency that eliminates the "it works on my machine" problem. On a VPS, Docker Compose can define your entire stack—Gunicorn, Nginx, PostgreSQL, Redis, and Celery—in a single YAML file, making deployments reproducible across development, staging, and production environments.
The Docker approach does consume additional RAM because each service runs in its own containerized environment, and Docker's overlay filesystem introduces a minor I/O overhead. For a VPS with 2 GB of RAM, a three-container setup (Django/Gunicorn, PostgreSQL, Nginx) is near the practical limit. Four GB of RAM is a more comfortable baseline for a containerized Django stack that includes Redis and Celery. The benefit is that dependency conflicts between services are impossible, and rolling back a deployment is as simple as pointing Docker Compose to the previous image tag.
One nuance worth mentioning: Django's collectstatic command should run during the Docker image build process, not at runtime, so that Nginx can serve static files from a shared volume without needing access to the Django container's filesystem. Similarly, database migrations should be executed as a separate Docker Compose service that runs to completion before the Gunicorn service starts, which prevents the application from serving requests against an outdated database schema. For teams that are still evaluating whether a VPS is overkill for their use case, our guide on dedicated servers explains the next step up when a VPS no longer suffices.
Once your Django application is live, you need visibility into how the VPS is coping with real traffic. Monitoring is not optional—it is the difference between noticing a slow memory leak at 2 AM and waking up to a downed site. At HostingCaptain, we recommend a three-layer monitoring approach: system metrics, application metrics, and database metrics.
Tools like Netdata, Prometheus with the Node Exporter, or even a lightweight setup with htop and iotop provide visibility into CPU utilization, memory consumption, disk I/O wait times, and network throughput. Set up alerts for memory usage crossing 80% and CPU steal time exceeding 5%, because steal time indicates that the hypervisor is overcommitted and your VPS is not getting the resources you are paying for. If steal time consistently exceeds 5%, migrate to a different physical host or switch to a provider that offers dedicated vCPU cores.
Sentry captures Django exceptions with full stack traces and local variable state, making it indispensable for debugging production errors. The Sentry SDK for Django integrates with a single line of configuration and automatically attaches request context to every exception. Pair Sentry with structured logging (emitting JSON logs to stdout, collected by journald or an external log aggregator) so that you can correlate application errors with system events.
Django's django.db.backends logger can output all SQL queries to the console, but in production you should use a dedicated tool like django-querycount or django-silk for profiling. Long-running queries can be identified with PostgreSQL's pg_stat_statements extension, which tracks query frequency, average execution time, and total time consumed. Query performance degradation is often the first sign that your application has outgrown its VPS tier, and upgrading RAM (to increase PostgreSQL shared buffers) or switching to NVMe storage is the most direct remediation.
Django applications typically scale vertically (more CPU and RAM on the same VPS) before they need horizontal scaling (multiple application servers behind a load balancer). Vertical scaling is simpler because it avoids the complexity of distributed sessions, shared media storage, and database connection pooling across servers. A VPS that can be resized with a reboot—a feature offered by DigitalOcean, Linode, and Vultr—lets you upgrade from 2 GB to 4 GB RAM in under five minutes. Horizontal scaling becomes necessary when you reach the CPU ceiling of a single server or require geographic distribution. At that point, you would deploy multiple Django application servers behind a load balancer, with a managed database service handling PostgreSQL, and S3-compatible object storage for user-uploaded media files.
No. Django requires a persistent WSGI or ASGI server process, system-level package installation, and the ability to bind to custom ports, none of which shared hosting provides. Shared hosting is designed for PHP applications that execute per-request, not for long-running Python servers.
A minimal Django deployment with two Gunicorn workers, PostgreSQL, Nginx, and Redis requires approximately 1.2 GB of RAM. For a production application with reasonable traffic, 2 GB is the practical minimum, and 4 GB provides comfortable headroom for Celery workers and database caching.
DigitalOcean, Linode, Vultr, and Hetzner all offer excellent VPS products for Django hosting. DigitalOcean provides the most comprehensive documentation and one-click setup; Linode offers higher RAM allocations at entry-level pricing; Vultr delivers the fastest CPU cores; and Hetzner provides the best price-to-performance ratio for European deployments.
Docker is recommended if you need environment consistency across development and production or if your stack includes multiple services (Celery, Redis, PostgreSQL, etc.). On a 2 GB VPS, a containerized Django stack is feasible but tight; 4 GB is the comfortable baseline for a multi-container Docker Compose setup.
Nginx should serve static files directly from disk, bypassing Django entirely. Media files (user uploads) can also be served by Nginx for small-to-medium applications, but as your site grows, offloading media to S3-compatible object storage reduces disk I/O pressure on the VPS and simplifies horizontal scaling.
PostgreSQL is the recommended database for Django. It supports advanced ORM features, JSON fields, full-text search, and a robust migration system. Install PostgreSQL directly on the VPS for small-to-medium projects, or use a managed database service when you need automated backups and failover.
Disable password SSH authentication, configure a firewall to allow only ports 22, 80, and 443, install fail2ban, enable unattended security updates, set all Django security middleware flags, and store sensitive credentials in environment variables. Automated off-server backups with encryption are essential for disaster recovery.
Yes, using Nginx virtual hosts and separate Gunicorn services for each site. Each Django project should have its own virtual environment, database, and Gunicorn socket. The total RAM requirement scales linearly with the number of projects, so a VPS hosting three small Django sites might need 3–4 GB of RAM.
Choosing the right vps for django app is a decision that affects everything from site speed to developer productivity. A well-configured 2 GB VPS with NVMe storage, PostgreSQL, and Nginx can comfortably serve tens of thousands of daily visitors, while a poorly chosen plan will have you troubleshooting 504 errors within the first week. At HostingCaptain, we have seen Django projects thrive on VPS infrastructure at every scale, and we believe that investing an extra hour in stack configuration pays dividends for years.
Emma Larsson is a lead systems developer and virtualization specialist with a decade of expertise in kernel configurations and hypervisor scaling.







