# Deploy MinIO + Iceberg with the minio-iceberg template

Source: https://docs.quake.ai/resources/deployments/deploy-minio-iceberg-template
Markdown: https://docs.quake.ai/resources/deployments/deploy-minio-iceberg-template.md

---

# Deploy MinIO + Iceberg with the minio-iceberg template

Stand up [MinIO](https://min.io) and an [Apache Iceberg](https://iceberg.apache.org) REST catalog on a single Quake AI instance using the [validated OpenTofu template](/docs/platform/validation#how-infrastructure-templates-are-checked) `minio-iceberg`. You apply the template, read the bootstrap credentials, confirm MinIO and the REST catalog respond, and create a namespace through the REST API.

You run this stack yourself; it is a self-hosted lakehouse catalog, not a managed warehouse service.

<Figure size="md" caption="What you will build: MinIO for table files and an Iceberg REST catalog on one instance, reachable over the floating IP from your workstation or a peer in the private network">

```d2
direction: right

dev: You {shape: person}
fip: Floating IP
instance: Ubuntu instance {
  minio: MinIO\nS3 API :9000
  rest: Iceberg REST\ncatalog :8181
  vol: Block volume\n/var/lib/docker
  rest -> minio: metadata commits
  minio -> vol: table files
}

dev -> fip: S3 + REST
fip -> instance
```

</Figure>

<PricingCompanion
  components={[
    { kind: "template", slug: "minio-iceberg", required: true },
  ]}
/>

## Prerequisites

You need:

- OpenTofu 1.6.0 or later (or Terraform 1.6.0 or later) installed locally.
- Your OpenStack credentials sourced into the shell (`source openrc.sh`). See [the OpenStack CLI guide](/docs/tools/openstack-cli).
- An SSH keypair that already exists in your project. Record its name for the `key_name` variable.
- A copy of the `minio-iceberg` template directory from [the template reference page](/resources/iac-templates/minio-iceberg).
- Your workstation's public IP address if you plan to reach MinIO and the REST catalog directly during setup. Find it with `curl -sS https://api.ipify.org`.

## Step 1: Set the variables and apply the template

MinIO and the REST catalog listen on ports 9000, 9001, and 8181. The template's security group restricts all three to `api_allowed_cidr`, which defaults to the private network only. To reach the services from your workstation during setup, set `api_allowed_cidr` to your own address.

Copy the template's example variables file and open it:

```bash
cp terraform.tfvars.example terraform.tfvars
```

Set `key_name` to the SSH keypair already in your project, and `api_allowed_cidr` to your workstation's public IP with a `/32` suffix:

```hcl
key_name           = "YOUR_KEY_NAME"
api_allowed_cidr   = "YOUR_IP/32"
```



Leave `api_allowed_cidr` at its default and tunnel over SSH instead:

```bash
ssh -L 9000:localhost:9000 -L 9001:localhost:9001 -L 8181:localhost:8181 ubuntu@YOUR_FLOATING_IP
```

Then use `http://localhost:9000` for the S3 API and `http://localhost:8181` for the REST catalog.



Initialize the working directory, preview the plan, and apply:

```bash
tofu init
tofu plan
tofu apply
```

OpenTofu provisions a private network, a router, a security group, a block volume mounted at `/var/lib/docker`, an instance, and a floating IP. On first boot, cloud-init mounts the data volume, installs Docker Engine, generates MinIO credentials, creates the warehouse bucket, and starts MinIO plus the REST catalog.

When the apply finishes, read the outputs:

```bash
tofu output
```

Record `floating_ip`, `minio_api_url`, `minio_console_url`, and `iceberg_rest_url`.

## Step 2: Read bootstrap credentials

No credential ships with the template. cloud-init generates MinIO root credentials on first boot and writes them to the instance.

cloud-init takes a few minutes after the instance reaches `ACTIVE`. SSH to the host and read the bootstrap file:

```bash
ssh ubuntu@YOUR_FLOATING_IP "sudo cat /opt/lakehouse/.bootstrap-credentials"
```

The file lists `minio_root_user`, `minio_root_password`, and `warehouse_bucket`. Use these values for MinIO console login and S3 client configuration.

Confirm both containers are running:

```bash
ssh ubuntu@YOUR_FLOATING_IP "sudo docker ps --filter name=lakehouse"
```

## Step 3: Verify MinIO and the REST catalog

Open `minio_console_url` (for example `http://YOUR_FLOATING_IP:9001`) in your browser and sign in with the bootstrap credentials.

From your workstation, confirm the REST catalog responds:

```bash
curl -sS "http://YOUR_FLOATING_IP:8181/v1/config" | head
```

The response includes catalog configuration. Next, create an Iceberg namespace through the REST API:

```bash
curl -sS -X POST "http://YOUR_FLOATING_IP:8181/v1/namespaces" \
  -H "Content-Type: application/json" \
  -d '{"namespace": ["analytics"], "properties": {}}'
```

List namespaces to confirm registration:

```bash
curl -sS "http://YOUR_FLOATING_IP:8181/v1/namespaces"
```

You now have an open Iceberg catalog backed by MinIO. Query engines such as Spark, Flink, or [Trino](/resources/iac-templates/trino) connect to `iceberg_rest_url` and read table metadata from the same warehouse URI.



The bundled MinIO instance stores files on the attached block volume. For a durable lake that other hosts can share, create a bucket in Quake AI [Object Storage](/docs/storage/object) and repoint the REST catalog warehouse at that bucket URI. See [How to create S3 credentials](/docs/storage/object/how-to/create-s3-credentials) for programmatic access.



## What you built

- **Applied the `minio-iceberg` template** to provision a network, security group, data volume, instance, and floating IP, and let cloud-init install Docker, MinIO, and the Iceberg REST catalog
- **Read bootstrap credentials** generated on first boot
- **Verified MinIO and the REST catalog** and registered an Iceberg namespace through the REST API

## Scope of this deployment

This template runs a single-VM lakehouse catalog and object store, not a managed warehouse. The instance is CPU-only and runs in one region. You operate MinIO, the REST catalog, and the data volume yourself: back them up, patch them, and watch disk use as table volume grows. For federated SQL over the tables you register here, add a [Trino](/resources/iac-templates/trino) query host that points at the same REST endpoint and warehouse URI.

## Next steps

- [MinIO + Iceberg lakehouse template](/resources/iac-templates/minio-iceberg): the template reference, parameters, and resource map
- [Trino query engine template](/resources/iac-templates/trino): federated SQL over Iceberg tables
- [S3 storage with ACLs](/resources/iac-templates/s3-storage-acl): provision a Quake AI Object Storage bucket as the durable lake target
- [How to create S3 credentials](/docs/storage/object/how-to/create-s3-credentials): programmatic access to Object Storage

## Clean up

When you no longer need the deployment, destroy everything the template created:

```bash
tofu destroy
```

Because MinIO object data and catalog state live on the instance and its attached volume, `tofu destroy` removes them along with the infrastructure. Export any tables or buckets you want to keep before you destroy.
