How to: Set up MinIO with apache2 reverse proxy

Mon, 11/09/2023 - 17:20 -- James Oakley
MinIO Logo

"MinIO is a high-performance, S3 compatible object store." So says their homepage.

In brief, it's a self-hosted suite of tools to let you run your own storage server that is fully compatible with the Amazon S3 standards. They have a free version licenced under GNU ACPL v3, and a commercial supported version with their own licence.

I wanted to set up my own storage system so I could cold-store files. For some uses, I want to keep control of my own data, and avoid the  costs of commercial services like Amazon S3, Microsoft Azure, Backblaze, Google Cloud, Wasabi and Dropbox.

The server I wanted to install it on was already running an apache2 webserver, so I wanted to access and control my storage setup by pointing a domain to that server. But I had a lot of trouble getting some of the steps to work, and their documentation is thin in places. So I'm posting this here to help others trying to solve similar problems

Step 1: Basic Setup

The first stage is to do a basic installation and setup. That's covered quite well on their "Getting Started" documentation page, with just one thing I was missing. This was for a server running Debian Linux.

  • Download the latest minio.deb file.
  • Run dpkg -i minio.deb

The documentation hints you may have to create a systemd file to run MinIO as a service. In fact, the installation creates one for you at the location they give you: /usr/lib/systemd/system/minio.service

We need to make one small tweak to that file. Out of the box, MinIO will start a process that listens on two ports. The first, default port 9000, is the S3-compatible service. The second, dynamically allocated at start time, runs the console website you'd use to create logins, buckets and so on if you want to use a GUI for this.

If you're going to administer this from the computer it runs on, you can simply browse to localhost:9000. MinIO will detect you're doing this in a web browser, and silently redirect you to localhost:{console-port} for whatever console port is in use at that point in time.

We're going to use an actual domain name to access the service. So we need to know what port the console application will run on, so we can reverse-proxy the domain to the right local port.

You can set the console to run on a static port number, but (unless I've missed this - they don't document it) this has to be done in the command line arguments used to start the service, and not in the configuration file you're meant to edit.

So edit that minio.service file, and change the ExecStart to read like this

ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES --console-address ":9001"

Obviously, use a different port number if you don't want 9001.

Step 2: Configuration

We now need to create two things, a configuration file, and a user account. The documentation page walks you through both. Again, there is one step that is not covered in that documentation.

Create a Linux user and group, and add the user to the group. Make sure these match the user and group specified in the systemd file

Create a file named /etc/default/minio that contains the root username and password, and the locations of the volumes where MinIO will store your data.

You also need to uncomment and set the MINIO_SERVER_URL argument. This is where you set the URL that will be used for the URLs to speak to the S3-compatible service. The commented out line in the documentation suggests Assuming you want to use a normal domain that you access normally in a browser, you probably want something like

Then the step not covered on this page of their documentation (references elsewhere point you to do this). You'll also want to access the web console from a real-world URL. If you don't set this up, and go to visit in a browser, you'll be redirected to, which is not what you want.

The argument you need to set is MINIO_BROWSER_REDIRECT_URL. So you'll need to add a line like this:


That's it. You've installed the MinIO software and set up the systemd file, the Linux user and group, and the configuration file. You should now be able to start the process:

systemctl start minio.service

Next, we need to set up the reverse proxy, so you can access this service via your chosen URLs. We've reached the end of their documentation page to get you started, and so this is the bit that is not so well documented on their website.

Step 3 Create the Reverse Proxy Virtual Hosts

There is a documentation page to walk you through this for nginx. They used to have one for apache as well, and it's linked from a few of their github issues, but the page no longer exists. You can access it via Internet Archive, but it was removed (and so not updated) before a crucial architecture change that means MinIO uses web sockets (more on that below).

You need to create two virtual hosts in your apache2 setup. How to do this is beyond the scope of this guide. Maybe you're using a control panel (cPanel, DirectAdmin, VirtualMin), or maybe you're editing configuration files by hand. Do what you need to do to create two new virtual hosts. The MinIO service and console app are running on localhost:9000 and localhost:9001, so these will not be serving up files stored on your server but will be proxy virtual hosts to serve up those local-listening web interfaces via publicly accessible domains.

Using the domains given above as examples, you'd proxy to and to

Do that, you'll be able to visit in your browser. It will redirect you to But you'll find two problems. You won't be able to log in, and you won't be able to browse objects in your storage.

To fix there are two changes you need to make. One can be found in that internet archive copy of an old documentation file, and the other needed more digging from elsewhere.

Step 4: ProxyPreserveHost

The first change to make is that you need to set the apache ProxyPreserveHost directive to "On".

Without this, you will get an error when you go to log in to the console.

The log in error

The request signature we calculated does not match the signature you provided. Check your key and signing method.

You need to set ProxyPreserveHost On in the virtual host configuration files (or sections) for both the service domain ( and the console domain (

You'll fix the login problems by making this change in the service domain alone, but you'll hit other problems later if you don't do the console domain too.

Step 5: WebSockets

In January 2023, MinIO made a change to use web sockets for the console and the service to talk to each other. There were documentation changes to the nginx instructions, but the apache instructions had been culled by then so never got updated.

If you don't adapt your apache configuration, you'll get errors when you try to use the object browser to browse files within your storage buckets. "Objects List unavailable. Please review your WebSockets configuration and try again." and "Couldn't establish WebSocket connection. Please review your configuration and try again."

Error when browsing files in a storage bucket

If you look at your apache access logs, you'll see lots of calls to /ws/objectManager failing with http status code 400, "400 Bad Request".

Firstly, you need to enable another apache module, if you haven't already got it: mod_proxy_wstunnel. Run a2enmod proxy_wstunnel, or enable the module using your server control panel if you have one.

Then you need to add some lines to the virtual host configuration file (or section) for the console domain ( I found lots of web pages online telling people how to adapt nginx instructions for proxy access to sockets, so they work for apache. Mostly, these were helping people with other services (not MinIO), so they could proxy a domain to a local port other than 9000.

Lots of those examples weren't quite right, and so there were a lot of dead-ends thinking I'd made the required changes without effect. I also tried other things, like switching ports 9000 and 9001 in the config changes.

But in fact, I wasn't quite there.

Here's the version that works. In the console virtual host, (which you'll remember in our example is proxied to port 9001) add this section:

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} WebSocket [NC]
    RewriteCond %{HTTP:CONNECTION} Upgrade [NC]
    RewriteRule .* ws://{REQUEST_URI} [P,L]

Restart apache (apachectl -t then systemctl restart apache2), and everything should work just fine.

Blog Category: 

Add new comment

Additional Terms