[{"data":1,"prerenderedAt":176},["ShallowReactive",2],{"seo-verification":3,"blog-loki-grafana-logs-centralises-vps-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"id":7,"slug":8,"title":9,"excerpt":10,"readTime":11,"views":12,"isPinned":13,"publishedAt":14,"category":15,"categories":21,"featuredImage":23,"bgImage":24,"posterImage":25,"relatedSolution":26,"intro":29,"sections":30,"ctaTitle":137,"ctaBody":138,"ctaButton":139,"ctaUrl":140,"relatedPosts":141},243,"loki-grafana-logs-centralises-vps","Centralize Docker logs on your VPS with Loki and Grafana","Aggregate logs from all your Docker containers into a single Grafana dashboard using Loki and Promtail on your VPS. No Datadog, no quota.",10,0,false,"2026-08-10T00:00:00+00:00",{"id":16,"name":17,"slug":18,"color":19,"icon":20},8,"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[22],{"id":16,"name":17,"slug":18,"color":19,"icon":20},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Floki-grafana-logs-centralises-vps-poster.svg",{"categorySlug":27,"appSlug":28},"monitoring","grafana","Jumping from one SSH session to another to read each container's logs takes time — the time needed to locate a 502 error that appeared across multiple services at once. Loki solves this by centralizing all your log streams into a single Grafana dashboard, with memory usage compatible with a standard VPS, where an ELK stack would require a dedicated server. This guide covers the complete deployment: Loki, Promtail, Grafana, and post-install configuration.",[31,35,47,50,81,84,87,90,98,101,134],{"type":32,"title":33,"body":34},"h2","Why centralize your logs on a VPS instead of staying with SSH","When an error hits multiple services simultaneously — a timeout on the application side, a 502 on nginx, an exception in a worker — no SSH tool lets you correlate these events without reading them one by one. `docker compose logs -f app`, then `docker compose logs -f nginx`, then `docker compose logs -f worker`: that's serial diagnosis, on a single server. On two VPS instances, the time doubles. On three, it triples.\n\nLoki is an open source log aggregation system developed by Grafana Labs and listed in the CNCF catalog. Unlike Elasticsearch, which indexes the full content of every log line, Loki only indexes **labels** — lightweight metadata such as the container name, service name, or environment. The logs themselves are compressed and stored as-is. This design choice has a direct consequence: Loki runs comfortably on a 2 GB RAM VPS, where a basic ELK stack requires at least 8 to 16 GB to remain stable. For a developer or sysadmin managing multiple self-hosted applications without an enterprise APM budget, that is the difference between a feasible solution and an impractical one.",{"type":36,"title":37,"items":38},"ul","What this stack delivers concretely",[39,40,41,42,43,44,45,46],"**Unified search across all containers** — a single LogQL query covers all services across all your VPS instances, without SSH or distributed `grep`.","**Temporal correlation** — Grafana displays Loki (logs) and Prometheus (metrics) on the same time axis: you see the 502 error and the CPU spike that preceded it in a single view.","**No ingestion quota** — you store as many logs as your disk allows, without a subscription or monthly limit imposed by a third party.","**Contained memory footprint** — Loki consumes significantly less RAM than Elasticsearch at equivalent log volume, thanks to label-only indexing.","**Configurable retention** — set retention duration per label (`chunk_retain_period`, `retention_period`) according to your disk constraints and compliance requirements.","**Native Grafana integration** — Loki is a first-class data source in Grafana: no third-party plugin, no intermediate API.","**Alerts on log content** — Grafana can trigger an alert when a regular expression matches in a Loki stream, without going through an external service.","**Data hosted on your infrastructure** — logs stay on your VPS, without transiting through a cloud service.",{"type":32,"title":48,"body":49},"Prerequisites before deploying","The Loki + Promtail + Grafana stack is lightweight, but it has its own requirements. Here is what you need before starting.\n\n**Recommended VPS resources**: 2 vCPU and 2 GB of RAM are a reasonable minimum for single-server usage with a few dozen active containers. If you collect logs from multiple VPS instances or a large number of services, aim for 4 GB of RAM. Loki does not keep logs in memory: it compresses them and writes them to disk, which keeps the memory footprint stable over time. Plan for 20 to 50 GB of SSD storage depending on your log volume and retention period.\n\n**Required software**: Docker and Docker Compose v2 installed on the VPS, a dedicated subdomain (`logs.your-domain.com`) pointing to the VPS IP address, and ports 3100 (Loki) and 3000 (Grafana) accessible internally. Port 3100 must **not** be exposed publicly — Loki has no native authentication layer, only Grafana is exposed via reverse proxy.",{"type":51,"title":52,"steps":53},"steps","Step-by-step deployment",[54,57,60,63,66,69,72,75,78],{"title":55,"body":56},"Create the directory structure","Connect to your VPS and create a dedicated directory: `mkdir -p \u002Fopt\u002Floki-stack\u002F{loki,promtail} && cd \u002Fopt\u002Floki-stack`. This folder will hold the Loki and Promtail configuration files along with the `docker-compose.yml`.",{"title":58,"body":59},"Write the Loki configuration","Create `\u002Fopt\u002Floki-stack\u002Floki\u002Floki-config.yaml` with the following content:\n\n`auth_enabled: false` — disables multi-tenant authentication, sufficient for single-VPS usage.\n\n`server: { http_listen_port: 3100 }`\n\n`ingester: { lifecycler: { address: 127.0.0.1, ring: { kvstore: { store: inmemory }, replication_factor: 1 } }, chunk_idle_period: 5m, chunk_retain_period: 30s }`\n\n`schema_config: { configs: [ { from: 2020-10-24, store: boltdb-shipper, object_store: filesystem, schema: v11, index: { prefix: index_, period: 24h } } ] }`\n\n`storage_config: { boltdb_shipper: { active_index_directory: \u002Floki\u002Fboltdb-shipper-active, cache_location: \u002Floki\u002Fboltdb-shipper-cache, shared_store: filesystem }, filesystem: { directory: \u002Floki\u002Fchunks } }`\n\n`limits_config: { retention_period: 720h }`",{"title":61,"body":62},"Write the Promtail configuration","Create `\u002Fopt\u002Floki-stack\u002Fpromtail\u002Fpromtail-config.yaml`. Promtail is the agent that collects logs from Docker containers and pushes them to Loki:\n\n`server: { http_listen_port: 9080, grpc_listen_port: 0 }`\n\n`positions: { filename: \u002Ftmp\u002Fpositions.yaml }`\n\n`clients: [ { url: http:\u002F\u002Floki:3100\u002Floki\u002Fapi\u002Fv1\u002Fpush } ]`\n\n`scrape_configs: [ { job_name: docker, docker_sd_configs: [ { host: unix:\u002F\u002F\u002Fvar\u002Frun\u002Fdocker.sock, refresh_interval: 5s } ], relabel_configs: [ { source_labels: ['__meta_docker_container_name'], regex: '\u002F(.*)', target_label: container }, { source_labels: ['__meta_docker_container_log_stream'], target_label: stream } ] } ]`\n\nThis configuration uses Docker auto-discovery (`docker_sd_configs`), which detects every started container without manual intervention.",{"title":64,"body":65},"Write the docker-compose.yml file","Create `\u002Fopt\u002Floki-stack\u002Fdocker-compose.yml`:\n\n`version: '3.8'`\n\n`services:`\n  `loki:` `image: grafana\u002Floki:3.0.0` `ports: ['127.0.0.1:3100:3100']` `volumes: [loki-data:\u002Floki, .\u002Floki\u002Floki-config.yaml:\u002Fetc\u002Floki\u002Flocal-config.yaml]` `command: -config.file=\u002Fetc\u002Floki\u002Flocal-config.yaml` `restart: unless-stopped`\n\n  `promtail:` `image: grafana\u002Fpromtail:3.0.0` `volumes: [\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock:ro, .\u002Fpromtail\u002Fpromtail-config.yaml:\u002Fetc\u002Fpromtail\u002Fconfig.yml, \u002Fvar\u002Flog:\u002Fvar\u002Flog:ro]` `command: -config.file=\u002Fetc\u002Fpromtail\u002Fconfig.yml` `restart: unless-stopped` `depends_on: [loki]`\n\n  `grafana:` `image: grafana\u002Fgrafana:latest` `ports: ['127.0.0.1:3000:3000']` `volumes: [grafana-data:\u002Fvar\u002Flib\u002Fgrafana]` `environment: [GF_SECURITY_ADMIN_PASSWORD=change-me]` `restart: unless-stopped` `depends_on: [loki]`\n\n`volumes:` `loki-data:` `grafana-data:`\n\nNote that Loki (`3100`) and Grafana (`3000`) are bound to `127.0.0.1` only — they are not directly accessible from outside.",{"title":67,"body":68},"Start the stack","From `\u002Fopt\u002Floki-stack`, run: `docker compose up -d`. Wait 20 to 30 seconds then check that all three containers are running: `docker compose ps`. Then verify the Loki logs to confirm it started without errors: `docker compose logs -f loki`. You should see a line `msg=\"Loki started\"` in the output.",{"title":70,"body":71},"Configure the reverse proxy for Grafana","Expose Grafana via nginx with a subdomain and TLS certificate. Create `\u002Fetc\u002Fnginx\u002Fsites-available\u002Fgrafana.conf`:\n\n`server { listen 443 ssl; server_name logs.your-domain.com; ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Flogs.your-domain.com\u002Ffullchain.pem; ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Flogs.your-domain.com\u002Fprivkey.pem; location \u002F { proxy_pass http:\u002F\u002F127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }`\n\nObtain the certificate with: `certbot certonly --nginx -d logs.your-domain.com`, then enable the site: `ln -s \u002Fetc\u002Fnginx\u002Fsites-available\u002Fgrafana.conf \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F && nginx -t && systemctl reload nginx`.",{"title":73,"body":74},"Add Loki as a data source in Grafana","Open `https:\u002F\u002Flogs.your-domain.com` in your browser. Log in with the `admin` username and the password you set in `GF_SECURITY_ADMIN_PASSWORD`. Go to **Connections → Data sources → Add data source**, choose **Loki**, and enter the URL: `http:\u002F\u002Floki:3100`. Click **Save & test** — you should see the message `Data source connected and labels found`.",{"title":76,"body":77},"Query your first logs","In Grafana, open the **Explore** tab and select the Loki source. Use the Log Browser to pick a `container` label and filter by container name. For example, to see all logs from the `nginx` container: `{container=\"nginx\"}`. To filter on a string: `{container=\"nginx\"} |= \"error\"`. To count errors by container over the last hour: `sum by (container) (count_over_time({container=~\".+\"} |= \"error\" [1h]))`.",{"title":79,"body":80},"Collect logs from a second VPS","To centralize logs from a second VPS into the same Loki instance, deploy only Promtail on that secondary VPS. In its configuration, replace the client URL with the internal address of your first VPS: `url: http:\u002F\u002F\u003CLoki-VPS-IP>:3100\u002Floki\u002Fapi\u002Fv1\u002Fpush`. Open port 3100 **only** between the two VPS instances (firewall or private network), never publicly. Add a static label to distinguish logs by server: `static_configs: [ { labels: { host: vps2 } } ]`.",{"type":32,"title":82,"body":83},"Post-install configuration: retention, backup, and alerts","Once the stack is running, three adjustments are important before considering the deployment complete.\n\n**Retention.** The `retention_period: 720h` value in `loki-config.yaml` corresponds to 30 days. Adjust this parameter based on your available disk space (`docker system df` to check volume usage). Loki applies retention in the background with no noticeable performance impact.\n\n**Backup.** The `loki-data` volume contains the BoltDB indexes and compressed chunks — it is the only element to back up. A daily snapshot of the Docker volume (`docker run --rm -v loki-data:\u002Fdata -v \u002Fbackup:\u002Fbackup alpine tar czf \u002Fbackup\u002Floki-$(date +%F).tar.gz \u002Fdata`) is sufficient for standard usage. Do not back up Grafana without its `grafana-data` volume (it contains your dashboards and alerts).\n\n**Alerts on log content.** In Grafana, create a **Grafana managed alert** on a Loki query: for example, trigger a notification when the number of lines containing `FATAL` exceeds 0 over the last 5 minutes. Configure a notification channel (email, Slack, webhook) in **Alerting → Contact points** before creating the rule.",{"type":85,"body":86},"tip","Loki exposes no native authentication mechanism on port 3100. If you need to expose the Loki API to an untrusted network (for remote Promtail agents, for example), place an nginx reverse proxy with client certificate authentication or basic auth upstream. Never leave port 3100 open on `0.0.0.0` on a production VPS. On the Grafana side, enable strong authentication: `GF_AUTH_ANONYMOUS_ENABLED=false` and `GF_USERS_ALLOW_SIGN_UP=false` in the container environment variables.",{"type":32,"title":88,"body":89},"Troubleshooting: common errors and how to resolve them","Here are the most frequent issues encountered when deploying the Loki + Promtail + Grafana stack.",{"type":36,"title":91,"items":92},"Common errors",[93,94,95,96,97],"**`msg=\"error creating ingester\" err=\"context deadline exceeded\"` on Loki startup** — the Loki container does not have write access to the mounted volume. Check the permissions of the directory corresponding to the Docker volume (`docker inspect loki-data` to find the actual path) and ensure the container user (UID 10001 for recent Grafana images) can write to it: `chown -R 10001:10001 \u002Fpath\u002Fto\u002Fthe\u002Fvolume`.","**`Data source connected and labels found` absent in Grafana, replaced by `connection refused`** — Grafana cannot reach Loki. Verify that the URL entered in the data source is `http:\u002F\u002Floki:3100` (the Docker service name, not `localhost`) and that both containers are in the same Docker network (`docker inspect loki-grafana-stack_default`).","**Promtail collects no logs, `docker compose logs -f promtail` shows `component=discovery.docker msg=\"refreshing targets\"` in a loop with no progress** — Promtail does not have access to the Docker socket. Verify that the volume `\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock:ro` is declared in the Promtail service and that the socket exists on the host: `ls -la \u002Fvar\u002Frun\u002Fdocker.sock`.","**`err=\"entry out of order for stream\"` in Loki logs** — logs are arriving with out-of-order timestamps (a restarted container resending old logs, for example). Add `max_stream_label_count: 0` under `limits_config` and `unordered_writes: true` under `ingester` in `loki-config.yaml` to accept out-of-sequence entries.","**Grafana shows `no data` on a valid LogQL query** — first check the time range selected in the top right (the most common trap: range set to `last 5 minutes` while Promtail has not yet collected recent logs). Then verify that the label used in your query actually exists: `{container=\"nginx\"}` fails if the container is named `nginx-1` — use the Log Browser to explore available labels.",{"type":32,"title":99,"body":100},"A dashboard to correlate logs and metrics","The main benefit of Loki in an existing Grafana environment is correlation with Prometheus metrics. If you already have a Prometheus source (see the article on VPS monitoring with Grafana and Prometheus), you can create a mixed dashboard: a row of metric panels (CPU, memory, HTTP request rate) at the top, and a Loki log panel filtered on the same service at the bottom. When a CPU spike appears at 2:37 PM, you immediately see which logs were emitted at that exact moment, without switching tools or running another SSH command. This type of correlation is the use case that justifies deploying both stacks rather than one or the other: Prometheus for \"what can be measured\", Loki for \"what gets narrated\".",{"type":102,"title":103,"headers":104,"rows":109},"comparison","Loki vs ELK vs SSH logs — which approach for which use case",[105,106,107,108],"Criterion","SSH + docker logs","ELK Stack (Elastic)","Loki + Grafana",[110,115,120,125,130],[111,112,113,114],"RAM required","None (no service)","8–16 GB minimum per node","2–4 GB for a standard VPS",[116,117,118,119],"Indexing","None","Full-text indexing (Elasticsearch)","Label-only indexing",[121,122,123,124],"Multi-server search","Impossible without scripting","Yes, natively","Yes, via multi-host Promtail",[126,127,128,129],"Correlation with metrics","Manual","With Kibana + APM (complex)","Native in Grafana",[131,117,132,133],"Software cost","Basic license free, features limited","Fully open source, no quota",{"type":32,"title":135,"body":136},"What you have just put into orbit","You now have a working log aggregation stack: Loki stores and indexes by labels, Promtail automatically collects streams from all your Docker containers, and Grafana provides a search and alerting interface. The stack runs entirely on your VPS, without a third-party service, without an ingestion quota, and without additional software cost.\n\nNatural next steps: connect this stack to your existing Prometheus monitoring for metrics\u002Flogs correlation, enable distributed tracing with Tempo if your applications emit OpenTelemetry traces, and explore Grafana alerts on log content to be notified before your users report a problem.","A VPS ready for your observability stack","Deploy Loki, Promtail, and Grafana on a ServOrbit Cloud VPS with full root access, dedicated IPv4, and NVMe SSD storage. No port restrictions, no software surcharge.","See Cloud VPS","\u002Fsolutions\u002Fdeveloppeurs",[142,155,166],{"id":143,"slug":144,"title":145,"excerpt":146,"readTime":147,"views":148,"isPinned":13,"publishedAt":149,"category":150,"categories":151,"featuredImage":23,"bgImage":24,"posterImage":153,"relatedSolution":154},106,"monitoring-vps-grafana-prometheus","VPS Monitoring with Grafana and Prometheus","Set up a Grafana + Prometheus stack on your VPS to collect, store and visualize your system and application metrics.",4,522,"2026-03-06T00:00:00+00:00",{"id":16,"name":17,"slug":18,"color":19,"icon":20},[152],{"id":16,"name":17,"slug":18,"color":19,"icon":20},"\u002Fblog\u002Fcovers\u002Fmonitoring-vps-grafana-prometheus-poster.svg",{"categorySlug":27,"appSlug":28},{"id":156,"slug":157,"title":158,"excerpt":159,"readTime":147,"views":12,"isPinned":13,"publishedAt":160,"category":161,"categories":162,"featuredImage":23,"bgImage":24,"posterImage":164,"relatedSolution":165},220,"opentelemetry-grafana-tempo-vps","Grafana Tempo and OpenTelemetry: distributed tracing on VPS","Deploy a full distributed tracing stack on your VPS with OpenTelemetry and Grafana Tempo, free from vendor lock-in and APM costs.","2026-08-04T00:00:00+00:00",{"id":16,"name":17,"slug":18,"color":19,"icon":20},[163],{"id":16,"name":17,"slug":18,"color":19,"icon":20},"\u002Fblog\u002Fcovers\u002Fopentelemetry-grafana-tempo-vps-poster.svg",{"categorySlug":27,"appSlug":28},{"id":167,"slug":168,"title":169,"excerpt":170,"readTime":11,"views":12,"isPinned":13,"publishedAt":171,"category":172,"categories":173,"featuredImage":23,"bgImage":24,"posterImage":175,"relatedSolution":23},228,"durcissement-serveur-linux-initial","Initial Linux Server Hardening","Create a sudo user, configure SSH with keys, enable UFW and fail2ban on Ubuntu 22.04 or Debian 12 in under an hour.","2026-08-06T00:00:00+00:00",{"id":16,"name":17,"slug":18,"color":19,"icon":20},[174],{"id":16,"name":17,"slug":18,"color":19,"icon":20},"\u002Fblog\u002Fcovers\u002Fdurcissement-serveur-linux-initial-poster.svg",1786366653994]