DEV Community

Muhammad Ilyasa Fadhlih
Muhammad Ilyasa Fadhlih

Posted on

Laravel 11+: Use Minio for file storage as AWS S3 free alternative

We might begin with stores our files like images on the filesystem. But when the times our application got bigger, we might want to separate things up, by using a dedicated service to stores objects.

For example, the most common way to store objects which is AWS's Simple Storage Service or S3 to be short. But that comes with the downside. Since it was a third-party service, we can't just use it for free. (Actually, they have free tier but with limited spaces)

Fortunately, there is a Free, Open Source and S3 Compatible alternative called Minio. With it, you can self-host the S3 compatible storage on your own.


Preparation


Minio

First, we need to have Minio, we can install it in a variety of ways. In this example, I will use Docker. If you don't want to use docker, you should check the Official Documentation.

# compose.yaml

services:
  Laravel:
    # build:
    #   context: .
    #
    networks:
      - my-network # use the same network as minio

  # Here's our minio
  storage:
    image: quay.io/minio/minio:RELEASE.2025-02-03T21-03-04Z
    restart: always
    command: ["server", "/data", "--console-address=:9001"]
    networks:
      - my-network # use the same network as your app
    env_file:
      - ./secrets/minio.env # we will specify this file later
    volumes:
      - object-storage:/data

networks:
  my-network: # declare a network

volumes:
  object-storage: # for persistent data
Enter fullscreen mode Exit fullscreen mode

Let me brief explain what's on the storage section above:

image: quay.io/minio/minio:RELEASE.2025-02-03T21-03-04Z:

for the image we are going to pull from the quay registry, and the tag we are going to use is RELEASE.2025-02-03T21-03-04Z.

You can use other tags too for example latest.

command: ["server", "/data", "--console-address=:9001"]:

first it will execute command server.

then the storage path will be on /data

the --console-address=:9001 meaning that will expose the console or the UI on the port 9001, while the API was exposed on port 9000 (by default)

We also need to specify the environment variable, but we're going to ignore it for now.


Laravel

On our Laravel's application, we need to install the AWS S3 packages using composer. Type the following on terminal:

composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies
Enter fullscreen mode Exit fullscreen mode

Configuration

We now need to do some configurations.

Minio

On Minio's side we need to create root user. We will later use that user to create a new user specifically for our application.

A root user is the highest level of access you can have on a computer system. It is essentially the "superuser" with the ability to perform any task, including those that are restricted to normal users.

To create that, we have to provide 2 variables:

  • MINIO_ROOT_USER: the name for the root user
  • MINIO_ROOT_PASSWORD: the password for the root user

If you take a look at the compose.yaml file above, you will see this:

    env_file:
      - ./secrets/minio.env
Enter fullscreen mode Exit fullscreen mode

Here, we are saying that the variables will be stored on a file called minio.env that is on secrets directory. Let's create one, make sure it's on the same level as the compose.yaml file.

create .env file for minio

mkdir secrets && cd $_ && touch minio.env
Enter fullscreen mode Exit fullscreen mode

On the new created minio.env file, fill with these:

MINIO_ROOT_USER=root # can be whatever you want
MINIO_ROOT_PASSWORD=insert-whatever-password
Enter fullscreen mode Exit fullscreen mode

Now, you can login to the Minio's Console.

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

On the browser, go to localhost:9000 or localhost:9001

Doesn't matter which port. If you use browser, minio will redirect you to port 9001 even if you type the port 9000

It's going to look like this.

Minio's login page

Login using the root username and password that we typed on the minio.env file previously.

Creating User

After successful login, go to the Identify -> User Section, and try to create new user.

User Section

Dont forget to check on the Read Write policy.

Create User Menu

Creating Bucket

Now, you need to create a bucket for your application.

What is a bucket?
Just that imagine that you have a bucket, then you can put your toys, your stuff in it. In this case we store our object like files or images.

Go to the Buckets section and click on the Create Bucket button

Type on the bucket name field, then click Create Bucket.

Bucket Section

Laravel

On Laravel you need to change the AWS variables.

AWS_ACCESS_KEY_ID=username
AWS_SECRET_ACCESS_KEY=password
Enter fullscreen mode Exit fullscreen mode

you can ignore the AWS_DEFAULT_REGION and let it be there

Fill the bucket name.

AWS_BUCKET=bucket-name-here
Enter fullscreen mode Exit fullscreen mode

And you need to change the AWS_ENDPOINT to point to your storage service.

AWS_ENDPOINT="http://storage:9000"
Enter fullscreen mode Exit fullscreen mode

Also you need to create another variable called AWS_URL. It's basically the endpoint with bucket name:

AWS_URL="http://storage:9000/bucket-name-here"
Enter fullscreen mode Exit fullscreen mode

Please note that I'm using the name service from the compose.yaml file. It's consider a bad practice for production, and instead you should use FQDN (Fully Qualified Domain Name). e.g https://storage.example.com

Last, but not least, you need to change the AWS_USE_PATH_STYLE_ENDPOINT to true

AWS_USE_PATH_STYLE_ENDPOINT=true
Enter fullscreen mode Exit fullscreen mode

Here's what our variables look like.

AWS_ACCESS_KEY_ID=username
AWS_SECRET_ACCESS_KEY=password
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=bucketname
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_ENDPOINT="http://storage:9000"
AWS_URL="http://storage:9000/bucketname"
Enter fullscreen mode Exit fullscreen mode

Now, you have to change they way you upload image.

From this.

// controller

$path = $request->file("image")->storePublicly("images");

$url = Storage::url($path);
Enter fullscreen mode Exit fullscreen mode

To this.

// controller

$request->file("image")->storePublicly("images", "s3");

$url = Storage::disk("s3")->url($path);
Enter fullscreen mode Exit fullscreen mode

Make bucket publicly accessible

You can't just paste the url on the browser, it won't let you to view the content of the file.

failed to get Stat() response from server for 1-5000x3333.jpg (version null): Access Denied.

Instead, you need to change the setting of the bucket so that everyone can see the content.

On the Buckets menu, click on your bucket, then change the Access Policy to public

Bucket Setting

Now, your image should be accessible.

Comment if you make it here!

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more