--- title: "Deploy Grafana Loki for centralized logging" --- This guide extends your existing [Prometheus and Grafana performance monitoring deployment](/administration-guide/scale/deploy-prometheus-grafana-for-performance-monitoring) by adding [Grafana Loki](https://grafana.com/oss/loki/) for centralized log aggregation. While Prometheus collects **metrics** (CPU, request latency, goroutine counts), Loki collects **logs** — giving your operations team the ability to search, filter, and correlate Mattermost application logs across all servers from a single Grafana interface. With Loki in place you can: - Search Mattermost logs across all application servers from Grafana. - Filter by log level, HTTP status code, or free-text search. - Identify the top recurring errors and warnings. - Correlate log events with Prometheus metric spikes on the same timeline. - Optionally collect PostgreSQL database logs alongside application logs. This guide assumes you have already deployed Prometheus and Grafana by following the [performance monitoring guide](/administration-guide/scale/deploy-prometheus-grafana-for-performance-monitoring). Loki and the OpenTelemetry Collector will be added to that existing infrastructure. ## Architecture overview The deployment adds two components to your monitoring stack: - **Loki** — the log aggregation engine, installed on your existing Grafana/Prometheus monitoring server. - **OpenTelemetry Collector** — a versatile, industry-standard agent installed on each Mattermost application server to ship logs. ``` text ┌──────────────────────┐ ┌──────────────────────┐ │ Mattermost App 01 │ │ Mattermost App 02 │ │ │ │ │ │ /opt/mattermost/ │ │ /opt/mattermost/ │ │ logs/mattermost.log│ │ logs/mattermost.log│ │ │ │ │ │ │ │ [ OTel Col ] │ │ [ OTel Col ] │ └─────────┬────────────┘ └─────────┬────────────┘ │ push (:3100/otlp) │ ▼ ▼ ┌─────────────────────────────────────────────────────┐ │ Monitoring Server │ │ │ │ [ Prometheus :9090 ] [ Loki :3100 ] │ │ │ │ │ │ └──────┬──────────────┘ │ │ ▼ │ │ [ Grafana :3000 ] │ │ Metrics + Logs │ └─────────────────────────────────────────────────────┘ Optional: Add the OpenTelemetry Collector on your PostgreSQL server to also ship database logs to Loki. ``` ## Prerequisites Before starting, confirm the following: - Prometheus and Grafana are deployed and collecting Mattermost metrics per the [performance monitoring guide](/administration-guide/scale/deploy-prometheus-grafana-for-performance-monitoring). - You have **SSH access** with sudo privileges to the monitoring server and each Mattermost application server. - Mattermost is installed at `/opt/mattermost` (the default location). If your installation path differs, substitute your path wherever `/opt/mattermost` appears. - **Network connectivity**: Each Mattermost server can reach the monitoring server on **TCP port 3100** (Loki's HTTP API). ## Step 1: Verify Mattermost JSON logging Mattermost must be writing JSON-formatted file logs for Loki queries to work. This is the default on all plans — verify it on each application server: ``` bash tail -1 /opt/mattermost/logs/mattermost.log ``` A JSON-formatted line looks like: ``` json {"timestamp":"2025-01-15T14:32:01.123Z","level":"info","msg":"Server is listening on [::]:8065","caller":"app/server.go:482"} ``` If you see plain-text output instead, go to **System Console \> Environment \> Logging** and set **Output file logs as JSON** to `true`, then restart Mattermost: ``` bash sudo systemctl restart mattermost ``` ## Step 2: Install Loki on the monitoring server Install Loki on the same server that runs Grafana and Prometheus. All commands in this section are run **on the monitoring server**. The commands below use Loki version **3.4.2**. Check the [Loki releases page](https://github.com/grafana/loki/releases) for the latest version and substitute the version number as needed. 1. Create the Loki user, directories, and download the binary: ``` bash # Create a dedicated system user sudo useradd --system --no-create-home --shell /bin/false loki # Create directories sudo mkdir -p /opt/loki/data /opt/loki/bin # Download and extract Loki sudo apt-get install -y unzip cd /tmp curl -LO https://github.com/grafana/loki/releases/download/v3.4.2/loki-linux-amd64.zip unzip loki-linux-amd64.zip sudo mv loki-linux-amd64 /opt/loki/bin/loki sudo chmod +x /opt/loki/bin/loki ``` 2. Download and install the production configuration file: - [Download loki-config.yaml](/files/samples/loki/loki-config.yaml) ``` bash sudo cp loki-config.yaml /opt/loki/loki-config.yaml ```
Tip
**Log retention** is set to **14 days** by default. To change this, edit `/opt/loki/loki-config.yaml` and update the `retention_period` value under `limits_config`. Loki requires this value in hours — common values: - `336h` = 14 days (default) - `720h` = 30 days - `2160h` = 90 days - `8760h` = 365 days Longer retention increases disk usage. As a rough guide, expect 1–3 GB per day for a moderately active Mattermost deployment (varies with log volume). Monitor `/opt/loki/data/` after the first week to project storage needs. The `compactor.retention_enabled` setting must remain `true` for retention enforcement to work.
3. Set ownership: ``` bash sudo chown -R loki:loki /opt/loki ``` 4. Create a systemd service file: ``` bash sudo tee /etc/systemd/system/loki.service > /dev/null <<'EOF' [Unit] Description=Grafana Loki Log Aggregation Documentation=https://grafana.com/docs/loki/latest/ After=network-online.target Wants=network-online.target [Service] Type=simple User=loki Group=loki ExecStart=/opt/loki/bin/loki -config.file=/opt/loki/loki-config.yaml Restart=on-failure RestartSec=5 LimitNOFILE=65536 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF ``` 5. Start Loki: ``` bash sudo systemctl daemon-reload sudo systemctl enable --now loki ``` 6. Verify Loki is running: ``` bash sudo systemctl status loki curl -s http://localhost:3100/ready ``` The `/ready` endpoint should return `ready`. Loki may take 15–20 seconds after startup before it reports ready — this warmup delay is normal. ## Step 3: Install OpenTelemetry Collector on each Mattermost server The OpenTelemetry (OTel) Collector runs on each Mattermost application server and pushes logs to Loki. Repeat these steps on **every Mattermost application server**. Replace `` with the IP address or hostname of your monitoring server, `` with this server's hostname (e.g., `mm-app-01`), and `` with the service type (e.g., `mattermost` or `postgres`). 1. Install the OTel Collector Contrib distribution: ``` bash # Add OTel Collector repository and install # Commands below are for Ubuntu/Debian wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.145.0/otelcol-contrib_0.145.0_linux_amd64.deb sudo dpkg -i otelcol-contrib_0.145.0_linux_amd64.deb ```
Note
The installation process automatically creates a system user and group named `otelcol-contrib`.
2. Download and edit the OpenTelemetry Collector configuration: - [Download otel-collector-config-mattermost.yaml](/files/samples/loki/otel-collector-config-mattermost.yaml) ``` bash sudo cp otel-collector-config-mattermost.yaml /etc/otelcol-contrib/config.yaml ``` Edit `/etc/otelcol-contrib/config.yaml` and replace the placeholders: - Replace `` with your monitoring server's address. - Replace `` with this server's hostname. - Replace `` with the service type (e.g., `mattermost`). ``` bash sudo vi /etc/otelcol-contrib/config.yaml ``` 3. Grant the collector read access to the Mattermost log directory: ``` bash # Grant the collector access to read Mattermost logs. # Substitute 'mattermost' for the group that owns your log file. sudo usermod -aG mattermost otelcol-contrib # Ensure logs are group-readable sudo chmod 640 /opt/mattermost/logs/mattermost.log sudo chmod g+rx /opt/mattermost/logs ``` 4. Restart the service: ``` bash sudo systemctl restart otelcol-contrib sudo systemctl enable otelcol-contrib ``` 5. Verify the collector is running and shipping logs: ``` bash sudo systemctl status otelcol-contrib sudo journalctl -u otelcol-contrib -f ``` ## Step 4: Add Loki as a data source in Grafana 1. Log in to Grafana (default: `http://:3000`). 2. Navigate to **Administration \> Data Sources \> Add data source**. 3. Select **Loki** from the list. 4. Configure the connection: - **Name**: `Loki` - **URL**: `http://localhost:3100` (since Loki is colocated with Grafana) 5. Select **Save & test**. Grafana should confirm the data source is working. ## Step 5: Import the Mattermost Loki dashboard A pre-built Grafana dashboard is recommended for monitoring your logs. This dashboard is provided as a basic example: - [Download the Mattermost Loki Logs dashboard JSON](/files/samples/grafana-dashboards/mattermost-loki-logs.json) While the dashboard provides a useful starting point, users can gain full query flexibility using the **Explore** tab in Grafana to build custom LogQL queries on the fly. The dashboard provides: - **Log Volume by Level** — stacked bar chart showing log rates by severity. - **Error / Warning / Total counters** — stat panels for the selected time range. - **HTTP 4xx/5xx count** — quick visibility into application-level HTTP errors. - **Error Rate Over Time** — time series per instance for spotting error spikes. - **Top Error Messages** — table ranking the most frequent errors. - **Log Browser** — searchable, filterable log panel with level and free-text variables. To import the dashboard: 1. In Grafana, go to **Dashboards \> New \> Import**. 2. Select **Upload JSON file** and choose the downloaded `mattermost-loki-logs.json` file. 3. On the import screen, select your **Loki** data source from the dropdown. 4. Select **Import**. ## Step 6: Useful LogQL queries You can run these queries directly in **Grafana Explore** (select the Loki data source) or use them to build additional dashboard panels. **Search all Mattermost logs:** ``` text {service_name="mattermost"} ``` **Filter by log level:** ``` text {service_name="mattermost"} | json | detected_level="error" ``` **Search for all HTTP 4xx responses:** ``` text {service_name="mattermost"} | json | status_code >= 400 ``` **Search for all HTTP 5xx responses:** ``` text {service_name="mattermost"} | json | status_code >= 500 ``` **Top 5 error messages over 5-minute windows:** ``` text topk(5, sum(count_over_time({service_name="mattermost"} | json | detected_level="error" [5m])) by (msg)) ``` **Count errors by message over 5-minute windows:** ``` text sum(count_over_time({service_name="mattermost"} | json | detected_level="error" [5m])) by (msg) ``` **Free-text search (e.g., plugin errors):** ``` text {service_name="mattermost"} |~ "plugin" ``` **Authentication-related log lines:** ``` text {service_name="mattermost"} |~ "(?i)(auth|login|token|session)" ``` **Logs from a specific server instance:** ``` text {service_name="mattermost", service_instance_id="mm-app-01"} ``` For a full LogQL reference, see the [Grafana Loki LogQL documentation](https://grafana.com/docs/loki/latest/query/). ## Optional: Add PostgreSQL log collection If your PostgreSQL database runs on a dedicated server (not RDS), you can ship its logs to Loki as well. This is useful for correlating slow queries or database errors with Mattermost application events. A separate configuration file is provided for PostgreSQL log collection. Install the OpenTelemetry Collector on the PostgreSQL server using the same steps from **Step 3**, then use this configuration file instead of the Mattermost one: - [Download otel-collector-config-postgres.yaml](/files/samples/loki/otel-collector-config-postgres.yaml) The PostgreSQL configuration handles common Ubuntu/Debian JSON log paths (`/var/log/postgresql/*.json`). Replace the same ``, ``, and `` placeholders as described in Step 3. Once running, PostgreSQL logs appear in Grafana under the label `{service_name="postgres"}`: ``` text {service_name="postgres"} | json | detected_level="error" ``` ## Verification and troubleshooting **End-to-end verification checklist:** 1. Loki is healthy: ``` bash curl -s http://:3100/ready # Expected: "ready" ``` 2. OpenTelemetry Collector is shipping logs (run on each Mattermost server): ``` bash sudo journalctl -u otelcol-contrib -n 100 # Look for export successful messages or lack of errors ``` 3. Logs are visible in Grafana: Go to **Explore**, select the **Loki** data source, and run `{service_name="mattermost"}`. You should see recent log lines. 4. The dashboard loads: Open the **Mattermost Log Aggregation** dashboard and confirm panels are populated. (Note: You may need to update dashboard queries to use service_name and service_instance_id). **Common issues:**
Symptom Resolution
OTel Col target shows errors Verify the file paths in the config match the actual log file location. Check file permissions (otelcol-contrib user must be able to read the file).
connection refused on port 3100 Ensure Loki is running (systemctl status loki). Check firewall rules: sudo iptables -L -n | grep 3100 or check your AWS security group allows TCP 3100 from the Mattermost servers.
Logs appear in Loki but fields aren't parsed Confirm Mattermost is writing JSON-formatted logs (see Step 1). Check that the json_parser operator (for Postgres) or OTel processors are correctly configured.
/ready returns an error Check Loki logs: sudo journalctl -u loki -f. Common cause: permissions on /opt/loki/data/ — ensure the loki user owns the directory.
Old logs are not being deleted Verify compactor.retention_enabled: true and limits_config.retention_period are both set in the Loki config. The compactor runs on the compaction_interval (default: 10 minutes) and applies a retention_delete_delay (default: 2 hours) before actually removing data.
High disk usage on the monitoring server Review the retention period in the configuration. Consider reducing it or adding more disk. Check du -sh /opt/loki/data/ to see current usage.