ALBANIA

ARGENTINA

AUSTRALIA

AUSTRIA

AZERBAIJAN

BANGLADESH

BELGIUM

BOSNIA AND HERZEGOVINA

BRAZIL

BULGARIA

CANADA

CHILE

CHINA

COLOMBIA

COSTA RICA

CROATIA

CYPRUS

CZECH REPUBLIC

DENMARK

ECUADOR

EGYPT

EL SALVADOR

ESTONIA

FINLAND

FRANCE

GEORGIA

GERMANY

GREECE

GUATEMALA

HUNGARY

ICELAND

INDIA

INDONESIA

IRELAND

ISRAEL

ITALY

JAPAN

KAZAKHSTAN

KENYA

KOSOVO

LATVIA

LIBYA

LITHUANIA

LUXEMBOURG

MALAYSIA

MALTA

MEXICO

MOLDOVA

MONTENEGRO

MOROCCO

NETHERLANDS

NEW ZEALAND

NIGERIA

NORWAY

PAKISTAN

PANAMA

PARAGUAY

PERU

PHILIPPINES

POLAND

PORTUGAL

QATAR

ROMANIA

RUSSIA

SAUDI ARABIA

SERBIA

SINGAPORE

SLOVAKIA

SLOVENIA

SOUTH AFRICA

SOUTH KOREA

SPAIN

SWEDEN

SWITZERLAND

TAIWAN

THAILAND

TUNISIA

TURKEY

UKRAINE

UNITED ARAB EMIRATES

UNITED KINGDOM

URUGUAY

USA

UZBEKISTAN

VIETNAM

How to Configure MinIO with Nginx as a Reverse Proxy on a Dedicated Server

Object storage has become the backbone of modern infrastructure, and if you're running your own dedicated server, setting up MinIO behind Nginx as a reverse proxy gives you a self-hosted, S3-compatible storage system that's fully under your control. This guide walks you through every step of the configuration process: from installing MinIO on bare metal to hardening it with SSL termination through Nginx.

Whether you're a sysadmin managing a high-performance dedicated server or a developer looking to replace expensive cloud storage with a private alternative, this tutorial is built specifically for your use case.

1. What Is MinIO and Why Use It on a Dedicated Server?

MinIO is an open-source, high-performance object storage server built for cloud-native workloads. It is fully compatible with the Amazon S3 API, which means any application that talks to S3 can talk to your self-hosted MinIO instance without code changes.

When deployed on a dedicated server, MinIO offers several advantages over managed cloud storage:

  • Full data sovereignty: your data stays on hardware you control

  • Predictable performance: no noisy-neighbor problems common in shared environments

  • Lower long-term cost: dedicated server hosting eliminates per-GB egress fees

  • S3 compatibility: drop-in replacement for AWS S3, Wasabi, Backblaze B2, and similar services

  • Horizontal scalability: MinIO supports distributed mode across multiple dedicated nodes

Using Nginx as a reverse proxy in front of MinIO adds a critical layer of capability: SSL/TLS termination, request buffering, virtual hosting, access logging, rate limiting, and a clean public endpoint. Instead of exposing MinIO's native port (9000) directly to the internet, Nginx acts as the gatekeeper, forwarding requests internally while presenting a standard HTTPS interface to the outside world.

COLO BIRD Tip: If you're colocating your own hardware or renting a bare-metal dedicated server, this setup gives you a production-grade private cloud storage system comparable to what enterprises pay thousands per month for in managed services.

2. Prerequisites and Server Requirements

Before you begin, make sure your dedicated server environment meets the following requirements.

Server Specifications

Component Minimum Recommended
CPU 2 cores 4+ cores
RAM 4 GB 8–16 GB
Storage 20 GB SSD NVMe SSD (capacity per your needs)
OS Ubuntu 22.04 LTS Ubuntu 22.04 / Debian 12
Network 100 Mbps 1 Gbps dedicated uplink

Software Requirements

  • A dedicated server with root or sudo access

  • A registered domain name pointed to your server's IP address (for SSL)

  • Ubuntu 22.04 LTS (commands in this guide are Debian/Ubuntu-based)

  • Open ports: 80 (HTTP), 443 (HTTPS), and optionally 9001 (MinIO Console)

Knowledge Prerequisites

  • Linux command-line basics

  • Basic understanding of web server configuration

  • SSH access to your dedicated server

3. Installing MinIO on Your Dedicated Server

Step 1: Update Your System

Start with a clean, updated base:

sudo apt update && sudo apt upgrade -y

Step 2: Create a Dedicated MinIO User

Running MinIO under its own system user is a security best practice on any dedicated server environment:

sudo useradd -r minio-user -s /sbin/nologin

Step 3: Create Storage and Config Directories

sudo mkdir -p /data/minio
sudo chown minio-user:minio-user /data/minio

sudo mkdir -p /etc/minio
sudo chown minio-user:minio-user /etc/minio

Step 4: Download and Install the MinIO Binary

MinIO releases a single static binary, no dependency hell, no package manager conflicts:

wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/

Verify the installation:

minio --version

Step 5: Create the MinIO Environment File

The environment file stores your credentials and configuration. Keep this file secure:

sudo nano /etc/minio/minio.env

Add the following content:

# MinIO Root Credentials
MINIO_ROOT_USER=your_admin_username
MINIO_ROOT_PASSWORD=your_strong_password_here

# Data directory
MINIO_VOLUMES="/data/minio"

# MinIO Options
MINIO_OPTS="--console-address :9001"

# Site name (optional but recommended)
MINIO_SITE_NAME="colobird-storage"

Set strict permissions on this file:

sudo chmod 600 /etc/minio/minio.env
sudo chown minio-user:minio-user /etc/minio/minio.env

Security Note: Never use default credentials. Choose a strong, randomly generated password for your MinIO root user, especially on a publicly accessible dedicated server.

4. Configuring MinIO as a Systemd Service

Running MinIO as a systemd service ensures it starts automatically on boot and restarts if it crashes, essential behavior for a production-grade dedicated server deployment.

Create the Systemd Unit File

sudo nano /etc/systemd/system/minio.service

Paste the following configuration:

[Unit]
Description=MinIO Object Storage Server
Documentation=https://min.io/docs/
Wants=network-online.target
After=network-online.target
AssertFileIsExecutable=/usr/local/bin/minio

[Service]
WorkingDirectory=/usr/local/

User=minio-user
Group=minio-user
ProtectProc=invisible

EnvironmentFile=-/etc/minio/minio.env
ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES

# Let systemd restart the service on failure
Restart=always
RestartSec=5

# Limit number of open files
LimitNOFILE=65536

# Disable timeout logic and the unnecessary notification
TimeoutStopSec=infinity
SendSIGKILL=no

[Install]
WantedBy=multi-user.target

Enable and Start MinIO

sudo systemctl daemon-reload
sudo systemctl enable minio
sudo systemctl start minio

Verify MinIO is running:

sudo systemctl status minio

You should see active (running) in the output. MinIO is now listening on port 9000 (API) and 9001 (web console) on your dedicated server.

5. Installing and Configuring Nginx as a Reverse Proxy

Nginx will serve as the public-facing gateway to your MinIO installation. All external requests hit Nginx on port 443 (HTTPS), and Nginx forwards them internally to MinIO on port 9000.

Step 1: Install Nginx

sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx

Step 2: Create the MinIO Nginx Configuration

sudo nano /etc/nginx/sites-available/minio

Paste the following configuration. Replace storage.yourdomain.com with your actual domain:

upstream minio_api {
    server 127.0.0.1:9000;
}

upstream minio_console {
    server 127.0.0.1:9001;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name storage.yourdomain.com console.yourdomain.com;

    return 301 https://$host$request_uri;
}

# MinIO API - S3-compatible endpoint
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name storage.yourdomain.com;

    # SSL certificates (configured by Certbot in the next step)
    ssl_certificate /etc/letsencrypt/live/storage.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/storage.yourdomain.com/privkey.pem;

    # SSL hardening
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;

    # Ignore MinIO-reported body size
    ignore_client_abort on;

    # Proxy to MinIO API
    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 300;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        chunked_transfer_encoding off;

        proxy_pass http://minio_api;
    }
}

# MinIO Console - Web UI
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;

    server_name console.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/console.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/console.yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_pass http://minio_console;
    }
}

Step 3: Enable the Configuration

sudo ln -s /etc/nginx/sites-available/minio /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

nginx -t validates your configuration before reloading. Always run this step to catch syntax errors before they take your service offline.

6. Securing MinIO with SSL/TLS via Let's Encrypt

On a publicly accessible dedicated server, running MinIO without HTTPS exposes your credentials and data to interception. Let's Encrypt provides free, auto-renewing SSL certificates.

Install Certbot

sudo apt install certbot python3-certbot-nginx -y

Obtain SSL Certificates

sudo certbot --nginx -d storage.yourdomain.com -d console.yourdomain.com

Follow the interactive prompts. Certbot will automatically modify your Nginx config to include the certificate paths and set up HTTPS redirects.

Verify Auto-Renewal

Let's Encrypt certificates expire every 90 days. Certbot sets up a cron job or systemd timer for automatic renewal. Test it with:

sudo certbot renew --dry-run

Your MinIO deployment is now accessible over HTTPS at https://storage.yourdomain.com.

7. Tuning Nginx for High-Throughput Object Storage

Default Nginx settings are not optimized for large file uploads and downloads typical of object storage workloads. Apply these tuning parameters to your Nginx configuration for better performance on a dedicated server with good hardware.

Edit the Main Nginx Config

sudo nano /etc/nginx/nginx.conf

Inside the http {} block, adjust or add:

http {
    # --- Worker settings ---
    worker_connections 4096;

    # --- Buffer tuning for large object transfers ---
    client_max_body_size 10G;       # Allow uploads up to 10 GB
    client_body_buffer_size 128k;
    client_body_timeout 300s;
    client_header_timeout 60s;

    # --- Proxy buffer settings ---
    proxy_buffering off;            # Disable buffering for streaming transfers
    proxy_request_buffering off;    # Pass request body directly to MinIO

    # --- Timeout settings for long-running uploads ---
    proxy_read_timeout 900s;
    proxy_send_timeout 900s;
    proxy_connect_timeout 60s;

    # --- Performance optimizations ---
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;

    # --- Compression (skip for already-compressed objects) ---
    gzip off;   # MinIO objects are often already compressed; gzip adds CPU overhead
}

Key Tuning Decisions Explained

  • proxy_buffering off - This is critical for MinIO. When buffering is on, Nginx stores the entire response from MinIO in memory/disk before sending it to the client. With large object storage transfers, this introduces latency and wastes RAM on your dedicated server. Disabling it lets data flow directly from MinIO to the client.

  • client_max_body_size 10G - The default is 1 MB, which blocks any upload above that size. Set this to the maximum object size you expect to store.

  • gzip off - Compressing already-compressed data (images, videos, archives) wastes CPU cycles without reducing file size.

Reload Nginx after making changes:

sudo nginx -t && sudo systemctl reload nginx

8. Firewall and Security Hardening

A dedicated server exposed to the internet requires a properly configured firewall. The goal is to allow only what's necessary and block everything else.

Configure UFW (Uncomplicated Firewall)

# Allow SSH (ensure this is first to avoid locking yourself out)
sudo ufw allow OpenSSH

# Allow HTTP and HTTPS for Nginx
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable the firewall
sudo ufw enable

# Verify rules
sudo ufw status verbose

Block Direct Access to MinIO Ports

MinIO's API (9000) and console (9001) ports should not be publicly accessible. All traffic should flow through Nginx. By default, UFW blocks these ports unless explicitly opened.

Confirm they are not exposed:

sudo ufw status | grep 9000
sudo ufw status | grep 9001

If either port appears as ALLOW, remove the rule:

sudo ufw delete allow 9000/tcp
sudo ufw delete allow 9001/tcp

Additional Hardening Recommendations

Fail2ban for brute force protection:

sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Fail2ban monitors your SSH and Nginx logs and automatically blocks IPs with repeated failed login attempts.

Disable root SSH login, Edit /etc/ssh/sshd_config and set:

PermitRootLogin no
PasswordAuthentication no

Then reload SSH: sudo systemctl reload ssh

9. Testing Your MinIO Deployment

Test 1: Verify Nginx Proxy is Working

From your local machine:

curl -I https://storage.yourdomain.com

You should receive an HTTP 403 response (correct, MinIO requires authentication) with no connection errors.

Test 2: Access the MinIO Console

Open https://console.yourdomain.com in your browser and log in with your root credentials. If you see the MinIO dashboard, your reverse proxy is working correctly.

Test 3: Use the MinIO Client (mc) for a Functional Test

Install the MinIO client on your local machine:

wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/

Configure it to point to your server:

mc alias set myserver https://storage.yourdomain.com your_admin_username your_strong_password

Create a test bucket and upload a file:

mc mb myserver/test-bucket
echo "Hello from COLO BIRD dedicated server" > test.txt
mc cp test.txt myserver/test-bucket/
mc ls myserver/test-bucket

If the file appears in the listing, your MinIO + Nginx stack is fully operational.

Test 4: S3 API Compatibility Check

If you have the AWS CLI installed:

aws s3 ls s3://test-bucket \
  --endpoint-url https://storage.yourdomain.com \
  --access-key your_admin_username \
  --secret-key your_strong_password

S3 API compatibility is one of MinIO's most valuable features for dedicated server deployments, any S3-compatible library or tool works out of the box.

10. Common Issues and Troubleshooting

Issue: Nginx Returns 502 Bad Gateway

Cause: Nginx cannot reach MinIO on port 9000.

Fix: Check MinIO's status:

sudo systemctl status minio
journalctl -u minio -n 50

Ensure MinIO is binding to 127.0.0.1:9000:

ss -tlnp | grep 9000

Issue: Large File Uploads Fail or Time Out

Cause: client_max_body_size is too low, or proxy timeout values are insufficient.

Fix: Increase client_max_body_size in your Nginx config and ensure proxy_read_timeout is set to at least 300 seconds. Reload Nginx after changes.

Issue: SSL Certificate Errors

Cause: Certificate paths in Nginx config don't match what Certbot generated.

Fix:

sudo certbot certificates

This lists all installed certificates and their paths. Update your Nginx config to match.

Issue: MinIO Console Websocket Disconnects

Cause: Nginx is not passing WebSocket upgrade headers to the console.

Fix: Ensure your console server block includes:

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Issue: Permission Denied on Data Directory

Cause: The /data/minio directory is not owned by minio-user.

Fix:

sudo chown -R minio-user:minio-user /data/minio
sudo systemctl restart minio

11. Next Steps: Scaling on Dedicated Infrastructure

You've successfully configured MinIO with Nginx as a reverse proxy on a dedicated server. Here are the logical next steps depending on your use case:

Distributed MinIO for High Availability

MinIO supports a distributed mode where data is spread across multiple dedicated servers using erasure coding. This provides fault tolerance — your data remains accessible even if one or more drives or nodes fail.

A distributed setup requires at least 4 MinIO nodes (or 4 drives). See the MinIO distributed mode documentation for the configuration details.

Monitoring with Prometheus and Grafana

MinIO exposes a /minio/health/live health endpoint and Prometheus-compatible metrics at /minio/v2/metrics/cluster. On a dedicated server environment, pairing MinIO with Prometheus + Grafana gives you real-time visibility into storage usage, request rates, and error counts.

Automated Backups with MinIO Replication

MinIO supports bucket replication, automatically copying objects to a second MinIO instance (or an AWS S3 bucket) for disaster recovery. This is particularly valuable for business-critical data stored on dedicated infrastructure.

Using MinIO with Applications

Since MinIO is fully S3-compatible, you can use it as the backend for:

  • WordPress media storage via S3-compatible plugins

  • Nextcloud as an external storage provider

  • GitLab for CI artifact and container registry storage

  • Backup tools like Restic, Duplicati, or Velero (for Kubernetes)

  • Any application using the AWS SDK, boto3, or s3cmd

Internal Linking Resources from COLO BIRD

If you found this guide useful, explore more resources on dedicated server management and optimization:

  • Choosing the Right Dedicated Server: Understanding CPU, RAM, and storage specifications for your workload

  • Dedicated Server Security Hardening: A comprehensive checklist for locking down bare-metal Linux servers

  • Setting Up a High-Availability Nginx Load Balancer: Route traffic across multiple dedicated nodes

  • NVMe vs SSD for Dedicated Server Storage: Performance benchmarks and cost analysis

Summary

Here is a quick recap of what this guide covered:

You installed MinIO on a dedicated server and configured it as a systemd service for automatic startup and process management. You then set up Nginx as a reverse proxy to handle incoming HTTPS traffic and forward it to MinIO's internal API and console ports. SSL/TLS encryption was added via Let's Encrypt, and you applied Nginx performance tuning for large object storage transfers. Finally, the server was hardened with a UFW firewall and Fail2ban to block unauthorized access.

The result is a production-ready, self-hosted S3-compatible object storage system running on your own dedicated hardware, giving you full data control, predictable performance, and significantly lower costs than equivalent managed cloud storage services.