In this tutorial we are gonna explore how to integrate MinIO as Object Storage with Golang.
So Let’s Start !
Requirements:
- Golang installed on your system. (golang 1.23.4 preferred)
- Docker and Docker compose installed on your system.
Step 1: Project init
Create a folder named “golang-minio-integration” wherever you want on your system. we call this project root from now on.
Step 2: Docker compose file
Inside your project root, create a file name “docker-compose.yml” with this content:
services:
goapp:
build: .
container_name: go_minio_app
ports:
- "8000:8000"
environment:
MINIO_ENDPOINT: minio-storage:9000
MINIO_ACCESS_KEY: admin123
MINIO_SECRET_KEY: admin123
depends_on:
minio-storage:
condition: service_healthy
minio-storage:
image: minio/minio
container_name: minio-storage
environment:
MINIO_ROOT_USER: admin123
MINIO_ROOT_PASSWORD: admin123
volumes:
- minio_data:/data
ports:
- "9000:9000"
- "9001:9001"
command: server --console-address ":9001" /data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/ready"]
interval: 5s
timeout: 3s
retries: 5
volumes:
minio_data:
This is a docker compose file and has two containers, one for minio self hosted and one for your golang project.
Step 3: Dockerfile
Inside your project root create another file named “Dockerfile” without any extensions with this content:
FROM golang:1.23.4-alpine
WORKDIR /app
COPY ./app/go.mod ./
RUN go mod download
COPY ./app .
RUN go build -o server main.go
CMD ["./server"]
Step 4: app directory
Inside project root, create a folder named “app”.
Step 5: go mod init
Open your terminal inside “app” directory and run this command:
go mod init github.com/mjmichael/golang-minio/integration
This will initialize a golang project in the “app” directory inside project root.
Step 6: MinIO dependency
Install the “minio” client library with this command:
go get "github.com/minio/minio-go/v7"
Step 7: minioclient package
Create a folder named “minioclient” inside the app directory.
Step 8: minio_client.go implementation
Create a file named “minio_client.go” inside the “minioclient” folder and put these content into it:
package minioclient
import (
"context"
"io"
"net/http"
"os"
"fmt"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
type MinIOClient struct {
Client *minio.Client
}
func NewMinIOClient() (*MinIOClient, error) {
endpoint := os.Getenv("MINIO_ENDPOINT")
accessKey := os.Getenv("MINIO_ACCESS_KEY")
secretKey := os.Getenv("MINIO_SECRET_KEY")
var err error
client, err := minio.New(endpoint, &minio.Options{
Creds: credentials.NewStaticV4(accessKey, secretKey, ""),
Secure: false,
})
if err != nil {
return nil, err
}
return &MinIOClient{
Client: client,
}, nil
}
func (m *MinIOClient) CreateBucket(bucketName string) error {
ctx := context.Background()
exists, err := m.Client.BucketExists(ctx, bucketName)
if err != nil {
return err
}
fmt.Println("Exists ", exists)
if !exists {
return m.Client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{})
}
return nil
}
func (m *MinIOClient) UploadFile(bucketName, objectName string, file io.Reader) error {
_, err := m.Client.PutObject(context.Background(), bucketName, objectName, file, -1, minio.PutObjectOptions{})
return err
}
func (m *MinIOClient) DownloadFile(bucketName, objectName string, w http.ResponseWriter) error {
object, err := m.Client.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
if err != nil {
return err
}
defer object.Close()
w.Header().Set("Content-Disposition", "attachment; filename="+objectName)
w.Header().Set("Content-Type", "application/octet-stream")
_, err = io.Copy(w, object)
return err
}
As you can see we are creating some functions for creating a bucket, uploading file and downloading file from “minio” object storage.
Step 9: main.go implementation
Now go back to “app” directory and create a file named “main.go” which is the entry point of your golang application, and put these contents in it:
package main
import (
"fmt"
"log"
"net/http"
"github.com/mjmichael/golang-minio-integration/minioclient"
)
func main() {
bucket := "mybucket"
minIOClient, err := minioclient.NewMinIOClient()
if err != nil {
fmt.Printf("can not create minio client: %v", err)
panic(err)
}
err = minIOClient.CreateBucket(bucket)
if err != nil {
fmt.Println(err)
fmt.Println("Error creating bucket ...")
panic(err)
}
http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error reading file", http.StatusBadRequest)
return
}
defer file.Close()
err = minIOClient.UploadFile(bucket, header.Filename, file)
if err != nil {
fmt.Println(err)
http.Error(w, "Failed to upload file", http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "File %s uploaded successfully.", header.Filename)
})
http.HandleFunc("/download/", func(w http.ResponseWriter, r *http.Request) {
fileName := r.URL.Path[len("/download/"):]
if fileName == "" {
http.Error(w, "Filename is not valid", http.StatusBadRequest)
return
}
err := minIOClient.DownloadFile(bucket, fileName, w)
if err != nil {
http.Error(w, "Failed to download file", http.StatusInternalServerError)
return
}
})
fmt.Println("Server started at :8000")
log.Fatal(http.ListenAndServe(":8000", nil))
}
As you can see first we are creating a minio client and then we are creating two handlers for upload and download, which these handlers use the “minioclient” package we defined earlier.
Step 10: Make it up and running !
Now everything is fine, let’s run the project by this command:
docker compose up --build -d
Step 11: Testing
Let’s test the project, to do this open a folder in your computer wherever you have a picture for example. I have a test.jpg file inside my computer, so I’ll go into the directory that contains this image and open terminal there, and then I run this command:
curl -F "[email protected]" http://localhost:8000/upload
And I get this result:

So as you can see the upload is working fine.
Now let’s test download, to test download, try to remove your test.jpg file from the directory.
And then run this command:
curl -O http://localhost:8000/download/test.jpg
And I get this:

I can see that the download has been completed and the file exist in my computer in the directory I ran these commands.
Summary:
So this is it !
By the way the code exists in my repo:
Leave a Reply