---
title: 🐳 Installation (Docker Compose)
---

Installation via Docker Compose is the simplest way to run the project across different environments. For it to run correctly, we need two main tools installed in our environment:

- Docker ([https://docs.docker.com](https://docs.docker.com/))
- Docker Compose ([https://docs.docker.com/compose](https://docs.docker.com/compose/))

> _It's worth emphasizing that Palmr. was fully developed in a MacOS environment and extensively tested on Linux servers. Therefore, we can guarantee the best system performance in these environments. Windows and other environments have not been tested yet, and potential bugs may occur during execution. However, remember that we are still in a beta version of Palmr., and errors or bugs can occur in any operating system. If you identify any issues, we appreciate your help in notifying us through our GitHub [issues page](https://github.com/kyantech/Palmr/issues)._

---

## ⚡ Quick Start

Having installed [Docker](https://docs.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) in our environment, we can proceed with the simple installation using these tools.

In the root folder of our project, we can find our `docker-compose.yaml` file, which is the only file needed to run the project via Docker and Docker Compose. This is because the pre-built images are already in our [DockerHub](https://hub.docker.com/repositories/kyantech) and are only referenced in the `docker-compose.yaml`

Any changes needed for execution can be made directly in our `docker-compose.yaml` or via environment variables, which we will show later in this tutorial.

Next, let's look at the content of our `docker-compose.yaml`.

---

## 🐳 Docker Compose Content

Below is the complete content of our `docker-compose.yaml` that can be copied directly from here or from our official repository ([Docker Compose](https://github.com/kyantech/Palmr/blob/main/docker-compose.yaml)).

```yaml
services:
  palmr:
    image: kyantech/palmr:latest # Make sure to use the correct version (latest) of the image
    container_name: palmr
    depends_on:
      postgres:
        condition: "service_healthy"
      minio:
        condition: "service_healthy"
    environment:
      # Server environment variables
      - PORT=${API_INTERNAL_PORT:-3333} # Port for the backend service
      - DATABASE_URL=postgresql://postgres:${POSTGRESQL_PASSWORD:-postgresRootPassword}@postgres:5432/palmr_db?schema=public # Database URL with configurable password through POSTGRESQL_PASSWORD env var
      - MINIO_ENDPOINT=${MINIO_ENDPOINT:-minio} # This can change if your MinIO is at a different address
      - MINIO_PORT=${MINIO_INTERNAL_API_PORT:-6421} # Default MinIO port (Change if yours is not the default)
      - MINIO_USE_SSL=false # MinIO uses SSL by default, but you can change it to true if needed
      - MINIO_ROOT_USER=${MINIO_ROOT_USER:-minio_root_user} # MinIO credentials can be configured through MINIO_ROOT_USER env vars
      - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioRootPassword} # MinIO credentials can be configured through MINIO_ROOT_PASSWORD env vars
      - MINIO_REGION=sa-east-1 # MinIO region - This is needed for MinIO to work properly
      - MINIO_BUCKET_NAME=files # MinIO bucket name - This is needed for MinIO to work properly, dont change it if you don't know what you are doing
      - FRONTEND_URL=${APP_URL:-http://${SERVER_IP:-localhost}:${APP_EXTERNAL_PORT:-5487}} # Frontend URL - Make sure to use the correct frontend URL, depends on where the frontend is running, its prepared for localhost, but you can change it to your frontend URL if needed
      - SERVER_IP=${SERVER_IP:-localhost} # Server IP - Make sure to use the correct server IP if you running on a cloud provider or a virtual machine. This prepared for localhost, but you can change it to your server IP if needed
      - MAX_FILESIZE=${MAX_FILESIZE:-1073741824} # Max Filesize for upload - Declared in Bytes. Default is 1GiB

      # Web environment variables
      - NODE_ENV=production
      - NEXT_TELEMETRY_DISABLED=1
      - API_BASE_URL=http://palmr:${API_INTERNAL_PORT:-3333} # Using Docker service name for internal communication
    ports:
      - "${API_EXTERNAL_PORT:-3333}:3333" # Server port
      - "${APP_EXTERNAL_PORT:-5487}:5487" # Web port
    restart: unless-stopped
    healthcheck:
      test:
        [
          "CMD",
          "wget",
          "--no-verbose",
          "http://palmr:${API_INTERNAL_PORT:-3333}/health",
          "&&",
          "wget",
          "--no-verbose",
          "http://palmr:${APP_INTERNAL_PORT:-5487}",
        ]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  minio:
    image: minio/minio:RELEASE.2025-03-12T18-04-18Z # Use only version RELEASE.2025-03-12T18-04-18Z to avoid compatibility issues with the backend
    container_name: minio
    environment:
      # MinIO credentials - same as above, configurable through environment variables
      - MINIO_ROOT_USER=${MINIO_ROOT_USER:-minio_root_user}
      - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-minioRootPassword}
      - MINIO_SITE_REGION=sa-east-1
    command: server /data --address ":${MINIO_INTERNAL_API_PORT:-6421}" --console-address ":${MINIO_INTERNAL_CONSOLE_PORT:-6422}"
    volumes:
      - minio_data:/data
    ports:
      - "${MINIO_EXTERNAL_API_PORT:-6421}:${MINIO_INTERNAL_API_PORT:-6421}"
      - "${MINIO_EXTERNAL_CONSOLE_PORT:-6422}:${MINIO_INTERNAL_CONSOLE_PORT:-6422}"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:${MINIO_INTERNAL_API_PORT:-6421}/minio/health/ready"]
      interval: 10s
      timeout: 5s
      retries: 5

  minio-init:
    image: minio/mc:RELEASE.2025-03-12T17-29-24Z # Use only version RELEASE.2025-03-12T17-29-24Z to avoid compatibility issues with the backend and MinIO
    container_name: minio-init
    depends_on:
      minio:
        condition: "service_healthy"
    restart: "no"
    # The entrypoint script will create a bucket called "files" and set it to be publicly readable using the MinIO client (mc).
    entrypoint: >
      sh -c "
        sleep 5 &&
        mc alias set myminio http://minio:${MINIO_INTERNAL_API_PORT:-6421} ${MINIO_ROOT_USER:-minio_root_user} ${MINIO_ROOT_PASSWORD:-minioRootPassword} &&
        mc mb myminio/files --ignore-existing &&
        mc anonymous set download myminio/files
      "

  postgres:
    image: bitnami/postgresql:17.2.0 # You can use any postgres version you prefer, but remember that some versions might not be compatible
    container_name: palmr-postgres
    environment:
      # PostgreSQL credentials configurable through environment variables
      # POSTGRESQL_USERNAME, POSTGRESQL_PASSWORD, and POSTGRES_DB can be set to override defaults
      - POSTGRESQL_USERNAME=${POSTGRESQL_USERNAME:-postgres}
      - POSTGRESQL_PASSWORD=${POSTGRESQL_PASSWORD:-postgresRootPassword}
      - POSTGRESQL_DATABASE=${POSTGRES_DATABASE:-palmr_db}
    volumes:
      - postgres_data:/bitnami/postgresql
    ports:
      - "5432:5432"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "palmr"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  minio_data:
  postgres_data:
```

Notice that the `docker-compose.yaml` has several comments that help you configure your own compose to meet your environment's needs. Let's give an overview of some changes we can make.

---

### ⚙️ Services Overview

Palmr. consists of four main services, each with specific responsibilities. Below, we present a detailed view of each component:

| **Service**         | **Image**                                                                                                                                                                                         | **Exposed Ports**                   | **Main Features**                                                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| palmr               | [kyantech/palmr:latest](https://hub.docker.com/repository/docker/kyantech/palmr/general)                                                                                                          | **3333** (API)<br/>**5487** (Web)   | • Combined backend API and frontend service<br/>• Depends on services: postgres and minio<br/>• Has healthcheck to ensure availability |
| minio (Storage)     | [minio/minio:RELEASE.2025-03-12T18-04-18Z](https://hub.docker.com/layers/minio/minio/RELEASE.2025-03-12T18-04-18Z/images/sha256-85f3e4cd1ca92a2711553ab79f222bcd8b75aa2c77a1a0b0ccf80d38e8ab2fe5) | **6421**(API)<br/>**6422**(Console) | • File storage service<br/>• Persistent volume for data                                                                                |
| minio-init (Config) | [minio/mc:RELEASE.2025-03-12T17-29-24Z](https://hub.docker.com/layers/minio/mc/RELEASE.2025-03-12T17-29-24Z/images/sha256-68d8c80f43908b02daa285e55547131870a1d36b3ffe272c26d7d8f4d52d1e5c)       | N/A                                 | • Initially configures the "files" bucket<br/>• Runs only once during initialization                                                   |
| postgres (Database) | [bitnami/postgresql:17.2.0](https://hub.docker.com/layers/bitnami/postgresql/17.2.0/images/sha256-29c614afad4f514b12b5c0f4d006f38c98fa4b18c3582732ff93b3fe9a79d875)                               | **5432**                            | • PostgreSQL database<br/>• Persistent volume for data                                                                                 |

---

### 🛠️ Available Environment Variables

The table below shows all environment variables that can be set

| **Variable**                | **Default Value**     | **Description**                                   |
| --------------------------- | --------------------- | ------------------------------------------------- |
| API_INTERNAL_PORT           | 3333                  | Internal API port in container                    |
| API_EXTERNAL_PORT           | 3333                  | Exposed port on host for API                      |
| POSTGRES_PASSWORD           | postgresRootPassword  | PostgreSQL database password                      |
| APP_EXTERNAL_PORT           | 5487                  | Exposed port on host for frontend                 |
| APP_URL                     | http://localhost:5487 | Complete frontend URL                             |
| SERVER_IP                   | localhost             | IP of the server where the application is running |
| MINIO_ROOT_USER             | minio_root_user       | MinIO admin user                                  |
| MINIO_ROOT_PASSWORD         | minioRootPassword     | MinIO admin password                              |
| MINIO_INTERNAL_API_PORT     | 6421                  | Internal MinIO API port                           |
| MINIO_INTERNAL_CONSOLE_PORT | 6422                  | Internal MinIO console port                       |
| MINIO_EXTERNAL_API_PORT     | 6421                  | Exposed port on host for MinIO API                |
| MINIO_EXTERNAL_CONSOLE_PORT | 6422                  | Exposed port on host for MinIO console            |
| POSTGRESQL_USERNAME         | postgres              | PostgreSQL user                                   |
| POSTGRESQL_DATABASE         | palmr_db              | Database name                                     |
| MAX_FILESIZE                | 1073741824            | Max Uploadsize per file. Unit in Bytes            |

> _All these variables can be configured through a .env file in the project root or defined directly in the environment where docker-compose will be executed. The best way to do this is up to you. But be careful to replace correctly if doing directly in the compose instead of providing an environment var._

#### 🗂️ Persistent Volumes

- minio_data: Stores MinIO files
- postgres_data: Stores PostgreSQL data

---

### 💻 Local Execution

In a localhost environment, there's no mystery. If you don't want to change any service exposure ports, you can simply run:

```bash
docker compose pull && docker compose up -d
```

This will execute all necessary services and give you access to the following URLs (if you haven't changed any ports):

- **Frontend:** [http://localhost:5487](http://localhost:5487)
- **Backend:** [http://localhost:3333](http://localhost:3333/)
- **MinIO API:** [http://localhost:6421](http://localhost:6421)
- **MinIO Console:** [http://localhost:6422](http://localhost:6422)
- **Postgres Database:** [http://localhost:5432](http://localhost:5432/) (Connection only)

> _If you have changed any port, simply access the URL with the port you configured._

---

### 🌐 Production (VPS or other)

For production environments, whether it's your VPS, Homelab Server, or other, the execution is very similar to the localhost environment, except for some particularities that may occur in some cases.

We mainly need to pay attention to the following points:

- Correctly set the `SERVER_IP` env var with our server's IP, otherwise some redirects and queries will fail during App execution.
- Set the `APP_URL` - regardless of whether the frontend is on the same server and with the same IP, it's extremely important to set this environment variable, otherwise sharing links will be generated incorrectly.
- For all environment variables that are `PASSWORD`, it's highly recommended to generate secure passwords and replace them as env vars.
- Lastly, make sure no docker service will conflict with any existing ones in your environment. If there is a conflict, simply change the execution ports via environment var or in the docker compose.

To generate a .env file with just the `server_ip` configuration, you can run this command:

```bash
curl -fsSL https://gist.githubusercontent.com/danielalves96/5a68913d70e5e31b68b7331dc17dfa9c/raw | bash
```

> execute this command in your server terminal, at same path of docker-compose.yaml.

Basically, by paying attention to these points, you can quickly execute the project with the same command we used for localhost:

```bash
docker compose pull && docker compose up -d
```

⚠️ This makes sure you're always running the latest beta version of the image, otherwise, Docker might reuse an outdated one from cache.

At this stage, if you encounter any errors, it's worth reviewing your `docker-compose.yaml` and trying again, paying close attention to the points mentioned above.

> _First test without using reverse proxies like Caddy, Traefik, etc... if you plan to use them. Access the services via `server_ip:port` after confirming they work, then make the necessary routing configurations as desired._

If you haven't changed the execution ports, you'll have access on your server at:

- **Frontend:** `[server_ip]:5487`
- **Backend:** `[server_ip]:3333`
- **MinIO API:** `[server_ip]:6421`
- **MinIO Console:** `[server_ip]:6422`
- **Postgres Database:** `[server_ip]:5432` (Connection only)

> _If you've changed any port, simply access the URL with the port you configured._

It's worth noting that this is just a quick start and we're not going into details about any of the developed services, but it's recommended for execution in any environment. However, if your focus is on using Palmr. with high availability in mind, it's recommended to use a container orchestrator prepared for this, such as Kubernetes or similar, but we don't cover this type of configuration in our documentation.

---

## 📚 Additional Resources

- [Docker Documentation](https://docs.docker.com/)
- [Docker Compose Documentation](https://docs.docker.com/compose/)
- [MinIO Documentation](https://min.io/docs/minio/container/index.html)
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
