TL;DR:
- Ubuntu 24.04 LTS ships everything in its default repos: Nginx 1.24, PostgreSQL 16, Redis 7.
- The full Ubuntu server setup — install, firewall, hardening, reverse proxy, SSL — takes about 20 minutes on a fresh VPS.
- UFW's
Nginx Fullprofile opens ports 80/443; PostgreSQL and Redis stay on localhost by default (keep them there). - Certbot adds a free Let's Encrypt certificate with auto-renewal in one command.
- Verify everything at once:
systemctl status nginx postgresql redis-server.
A fresh Ubuntu 24.04 VPS is a blank slate. This Ubuntu server setup guide takes you from first SSH login to Nginx serving requests, PostgreSQL storing data, and Redis handling caching or session state — configured correctly and ready for a production workload, within 20 minutes.
Commands are shown in order. Run them as root or prefix each with sudo.
Before you start
You need a VPS running Ubuntu 24.04 LTS with root or sudo access. If you're still on shared hosting and running into resource limits, the gap between what shared hosting provides and what a VPS gives you is real — moving to a VPS makes sense once your workload demands consistent CPU and memory.
If you're deploying on Arct Cloud, your server is SSH-ready in under 60 seconds. Once you have your IP and root credentials, you're ready to go.
Step 1: Update the system
Start with a full package update before installing anything.
apt update && apt upgrade -y
This ensures you're installing against the latest package index and avoids dependency conflicts from stale metadata.
Step 2: Install Nginx on Ubuntu 24.04
Ubuntu 24.04's default repositories include Nginx 1.24. Install it and enable it to start on boot.
apt install nginx -y
systemctl enable --now nginx
Verify it's running:
systemctl status nginx
You should see active (running). Open your server's IP in a browser and you'll get the default Nginx welcome page.
Configure the firewall
If you're running UFW, allow HTTP and HTTPS traffic:
ufw allow 'Nginx Full'
ufw enable
ufw status
Nginx Full opens ports 80 and 443. If you only need HTTP for now, use Nginx HTTP instead.
Step 3: Install PostgreSQL 16 on Ubuntu 24.04
Ubuntu 24.04 ships with PostgreSQL 16. Install it directly from the default repos:
apt install postgresql postgresql-contrib -y
systemctl enable --now postgresql
Confirm the service is active:
systemctl status postgresql
Create a database and user
PostgreSQL creates a system user called postgres during installation. Switch to it and open the interactive shell:
su - postgres
psql
Inside psql, create a dedicated user and database for your application:
CREATE USER appuser WITH PASSWORD 'your_secure_password';
CREATE DATABASE appdb OWNER appuser;
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;
\q
Exit back to root:
exit
Configure remote access (optional)
By default, PostgreSQL only listens on localhost. If your application runs on the same server, leave it that way. If you need remote access, edit postgresql.conf:
nano /etc/postgresql/16/main/postgresql.conf
Find the listen_addresses line and update it:
listen_addresses = '*'
Then edit pg_hba.conf to allow your specific IP:
nano /etc/postgresql/16/main/pg_hba.conf
Add a line at the bottom:
host appdb appuser your.client.ip/32 scrypt
Restart PostgreSQL to apply the changes:
systemctl restart postgresql
Only open remote access if you genuinely need it. A database exposed to the public internet is an unnecessary risk.
Step 4: Install Redis 7 on Ubuntu 24.04
Redis 7 is available in Ubuntu 24.04's repositories.
apt install redis-server -y
systemctl enable --now redis-server
Verify it responds:
redis-cli ping
Expected output: PONG
Harden the Redis configuration
The default Redis config binds to localhost and has no password. For a single-server setup, the localhost binding is correct. Add a password for any environment where Redis handles sensitive session data.
Open the config file:
nano /etc/redis/redis.conf
Find the requirepass line, uncomment it, and set a strong password:
requirepass your_redis_password
Confirm Redis is bound to localhost only — this is the default, but verify it:
bind 127.0.0.1 ::1
Restart Redis:
systemctl restart redis-server
Test authentication:
redis-cli -a your_redis_password ping
Step 5: Configure Nginx as a reverse proxy
For most web applications, Nginx sits in front of your app server (Node.js, Gunicorn, Puma, etc.) and proxies requests to it. Here's a basic server block for an app running on port 3000:
nano /etc/nginx/sites-available/myapp
Paste the following:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable the site and test the config:
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t
If the test returns syntax is ok and test is successful, reload Nginx:
systemctl reload nginx
Step 6: Verify all 3 services are running
Check everything at once:
systemctl status nginx postgresql redis-server
All 3 should show active (running). If any show failed, check the journal:
journalctl -xeu nginx
journalctl -xeu postgresql
journalctl -xeu redis-server
The journal output will point to the exact config line that caused the failure.
Step 7: Add SSL with Let's Encrypt
Serving over HTTP in production is not acceptable. Install Certbot and get a certificate:
apt install certbot python3-certbot-nginx -y
certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot modifies your Nginx config automatically, installs the certificate, and sets up auto-renewal via a systemd timer. Verify the renewal timer is active:
systemctl status certbot.timer
Keeping the stack healthy
A few practices worth following once the stack is live:
- Monitor disk I/O — PostgreSQL write-heavy workloads benefit from NVMe-backed storage. On Arct Cloud, every VM runs on Gen4 NVMe delivering up to 7,000 MB/s, so disk is rarely the bottleneck.
- Set Redis maxmemory — Add
maxmemory 256mbandmaxmemory-policy allkeys-lrutoredis.confif Redis is used for caching. Without a memory limit, Redis will consume all available RAM under load. - Enable PostgreSQL logging — Set
log_min_duration_statement = 1000inpostgresql.confto log any query taking over 1 second. Slow query logs are the fastest way to catch performance problems early. - Back up your database —
pg_dump appdb > appdb_backup.sqlgives you a portable snapshot. Automate it with a cron job and store backups off-server.
FAQs
What version of PostgreSQL ships with Ubuntu 24.04? Ubuntu 24.04 LTS ships with PostgreSQL 16 in its default repositories. You can install newer versions from the official PostgreSQL APT repository if your application requires them.
Should Redis bind to localhost or a public IP?
Bind Redis to localhost (127.0.0.1) unless your architecture specifically requires Redis to be reachable from another server. Exposing Redis on a public interface without strict firewall rules is a common misconfiguration that leads to data exposure.
How do I connect my application to PostgreSQL?
Use the credentials you created in Step 3. Your connection string will follow the pattern postgresql://appuser:your_secure_password@localhost:5432/appdb. Most application frameworks accept this format directly.
Can Nginx serve static files and proxy dynamic requests at the same time?
Yes. Add a location /static/ block pointing to your static file directory before the proxy location / block. Nginx serves static files directly without touching your app server, which reduces load significantly.
How do I test that Redis is caching correctly?
Run redis-cli monitor while sending requests to your application — you'll see every command Redis receives in real time. For cache hit/miss ratios, run redis-cli info stats and check keyspace_hits and keyspace_misses.
What's the right way to run multiple sites on one Nginx server?
Create a separate config file in /etc/nginx/sites-available/ for each site with a unique server_name directive. Symlink each to sites-enabled/, run nginx -t, and reload. Nginx routes requests by matching the Host header against each server_name.
Do I need to open PostgreSQL's port in UFW? Only if your application connects from a different server. For a single-server stack where Nginx, your app, and PostgreSQL all run on the same VPS, PostgreSQL stays on localhost and UFW doesn't need a rule for port 5432.
Your stack is running. Nginx handles incoming traffic, PostgreSQL stores your data, and Redis handles caching or sessions. The next step is deploying your application code and pointing it at these services using the credentials and socket paths configured above.
If you're still deciding where to host, Arct Cloud provisions Ubuntu 24.04 VMs in under 60 seconds with full root access, Gen4 NVMe storage, and a 10 Gbps uplink — everything this stack needs, with no managed-runtime layer in the way.