# [[Deploying Loki on AWS]] ![[Deploying Loki on AWS.svg]] [docs](https://grafana.com/docs/loki/latest/setup/install/helm/deployment-guides/aws/) [[Grafana Loki|Loki]] can be deployed on a [[Kubernetes]] cluster on [[AWS]] using: - [[AWS Simple Storage Service (S3)]] as a storage for Loki - [[AWS Elastic Kubernetes Service|EKS]] running on [[Amazon EC2]] nodes ([[AWS Fargate]] is an alternative for a more [[Serverless computing|serverless]] experience) - [[Helm for Kubernetes|Helm]] charts to handle the configuration ![](https://www.youtube.com/watch?v=5lXmWmofqwM) [^1] ## Create cluster First, spin up the Kubernetes cluster within EKS and the worker nodes that will hold them by using this clsuter config file: ```yaml # A simple example of ClusterConfig object: --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: <INSERT-CLUSTER-NAME> region: <INSERT-REGION-FOR-CLUSTER> version: "1.31" iam: withOIDC: true addons: - name: aws-ebs-csi-driver managedNodeGroups: - name: loki-workers instanceType: m7i.2xlarge desiredCapacity: 3 minSize: 2 maxSize: 3 amiFamily: AmazonLinux2023 iam: withAddonPolicies: ebs: true volumeSize: 80 volumeType: gp3 ebsOptimized: true ``` The config file above does the following: - Starts 3 instances of `m7i.2xlarge` nodes (8 cores, 32 GB RAM, 10 Gb/s EBS bandwidth) - Installs a plugin, [[Amazon EBS CBI driver]] lets Kubernetes provision and use [[Amazon EBS|EBS]] volumes as persistent volumes for Loki. > [!tip]- Other plugins required > [[Core DNS]] (ensures services and pods can talk to each other via [[DNS]]) and [[Kube Proxy]] (enables communication between nodes and pods) are also required, but should be provided by default. Second, create the cluster by running this command using [[EKS CTL]]: ```bash eksctl create cluster -f cluster.yml ``` ## Create S3 buckets for Loki storage We need two S3 buckets: one for storing [[Loki Chunks|chunks]] and another for storing [[Loki ruler|rules]]. Run the following command using the [[AWS CLI]]: ```bash aws s3api create-bucket --bucket <YOUR CHUNK BUCKET NAME e.g. `loki-aws-dev-chunks`> --region <S3 region your account is on, e.g. `eu-west-2`> --create-bucket-configuration LocationConstraint=<S3 region your account is on, e.g. `eu-west-2`> \ aws s3api create-bucket --bucket <YOUR RULER BUCKET NAME e.g. `loki-aws-dev-ruler`> --region <S3 REGION your account is on, e.g. `eu-west-2`> --create-bucket-configuration LocationConstraint=<S3 REGION your account is on, e.g. `eu-west-2`> ``` Make sure to replace `region` and `bucket` with the appropriate values. ## Set permissions for Loki to access S3 Using [[IAM]] roles to connect Loki to S3 is more secure than the alternative, which is to use access keys and secret keys stored directly in Loki config. ### Create an IAM policy JSON file IAM policies are set using a [[JSON]] file like this: ```json { "Version": "2012-10-17", "Statement": [ { "Sid": "LokiStorage", "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::< CHUNK BUCKET NAME >", "arn:aws:s3:::< CHUNK BUCKET NAME >/*", "arn:aws:s3:::< RULER BUCKET NAME >", "arn:aws:s3:::< RULER BUCKET NAME >/*" ] } ] } ``` Make sure you update the bucket names in the `Resource` section. ### Create the IAM policy Run this using AWS CLI: ```bash aws iam create-policy --policy-name LokiS3AccessPolicy --policy-document file://loki-s3-policy.json ``` ### Create a trust policy JSON file ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::< ACCOUNT ID >:oidc-provider/oidc.eks.<INSERT REGION>.amazonaws.com/id/< OIDC ID >" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.<INSERT REGION>.amazonaws.com/id/< OIDC ID >:sub": "system:serviceaccount:loki:loki", "oidc.eks.<INSERT REGION>.amazonaws.com/id/< OIDC ID >:aud": "sts.amazonaws.com" } } } ] } ``` Make sure you use your AWS account ID, region, and OIDC ID (which you can find in the EKS cluster configuration) with the appropriate values. ### Create the IAM role ```bash aws iam create-role --role-name LokiServiceAccountRole --assume-role-policy-document file://trust-policy.json ``` ### Attach the IAM policy to the IAM role ``` aws iam attach-role-policy --role-name LokiServiceAccountRole --policy-arn arn:aws:iam::<Account ID>:policy/LokiS3AccessPolicy ``` ## Add authentication for Loki (optional but recommended) Deploying Loki on AWS will mean it is accessible to anyone on the internet, so it's recommended to at least use basic authentication for Loki. ### Create namespace for Loki Create a new namespace for Loki to use: ```bash kubectl create namespace loki ``` ### Install `htpasswd` ```bash sudo apt-get update && sudo apt-get install apache2-utils ``` ### Create `auth` file Create an `auth` file by running this command: ```bash htpasswd -c .htpasswd <username> ``` When prompted, enter a password. ### Create a Kubernetes secret ```bash kubectl create secret generic loki-basic-auth --from-file=.htpasswd -n loki ``` ### Create a `canary-basic-auth` secret ```bash kubectl create secret generic canary-basic-auth \ --from-literal=username=<USERNAME> \ --from-literal=password=<PASSWORD> \ -n loki ``` ## Deploy Loki via Helm ### Add Grafana chart repository to Helm Make sure you've installed [[Helm for Kubernetes|Helm]]. Then run the following: ```bash helm repo add grafana https://grafana.github.io/helm-charts ``` Update the repo: ```bash helm repo add grafana https://grafana.github.io/helm-charts ``` ### Create the Helm chart config file ```yaml loki: schemaConfig: configs: - from: "2024-04-01" store: tsdb object_store: s3 schema: v13 index: prefix: loki_index_ period: 24h storage_config: aws: region: <S3 BUCKET REGION> # for example, eu-west-2 bucketnames: <CHUNK BUCKET NAME> # Your actual S3 bucket name, for example, loki-aws-dev-chunks s3forcepathstyle: false ingester: chunk_encoding: snappy pattern_ingester: enabled: true limits_config: allow_structured_metadata: true volume_enabled: true retention_period: 672h # 28 days retention compactor: retention_enabled: true delete_request_store: s3 ruler: enable_api: true storage: type: s3 s3: region: <S3 BUCKET REGION> # for example, eu-west-2 bucketnames: <RULER BUCKET NAME> # Your actual S3 bucket name, for example, loki-aws-dev-ruler s3forcepathstyle: false alertmanager_url: http://prom:9093 # The URL of the Alertmanager to send alerts (Prometheus, Mimir, etc.) querier: max_concurrent: 4 storage: type: s3 bucketNames: chunks: "<CHUNK BUCKET NAME>" # Your actual S3 bucket name (loki-aws-dev-chunks) ruler: "<RULER BUCKET NAME>" # Your actual S3 bucket name (loki-aws-dev-ruler) # admin: "<Insert s3 bucket name>" # Your actual S3 bucket name (loki-aws-dev-admin) - GEL customers only s3: region: <S3 BUCKET REGION> # eu-west-2 #insecure: false # s3forcepathstyle: false serviceAccount: create: true annotations: "eks.amazonaws.com/role-arn": "arn:aws:iam::<Account ID>:role/LokiServiceAccountRole" # The service role you created deploymentMode: Distributed ingester: replicas: 3 zoneAwareReplication: enabled: false querier: replicas: 3 maxUnavailable: 2 queryFrontend: replicas: 2 maxUnavailable: 1 queryScheduler: replicas: 2 distributor: replicas: 3 maxUnavailable: 2 compactor: replicas: 1 indexGateway: replicas: 2 maxUnavailable: 1 ruler: replicas: 1 maxUnavailable: 1 # This exposes the Loki gateway so it can be written to and queried externaly gateway: service: type: LoadBalancer basicAuth: enabled: true existingSecret: loki-basic-auth # Since we are using basic auth, we need to pass the username and password to the canary lokiCanary: extraArgs: - -pass=$(LOKI_PASS) - -user=$(LOKI_USER) extraEnv: - name: LOKI_PASS valueFrom: secretKeyRef: name: canary-basic-auth key: password - name: LOKI_USER valueFrom: secretKeyRef: name: canary-basic-auth key: username # Enable minio for storage minio: enabled: false backend: replicas: 0 read: replicas: 0 write: replicas: 0 singleBinary: replicas: 0 ``` ### Deploy Loki ```bash helm install --values values.yaml loki grafana/loki -n loki --create-namespace ``` %% # Excalidraw Data ## Text Elements ## Drawing ```json { "type": "excalidraw", "version": 2, "source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/2.1.4", "elements": [ { "id": "4y8R7iOA", "type": "text", "x": 118.49495565891266, "y": -333.44393157958984, "width": 3.8599853515625, "height": 24, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, "roundness": null, "seed": 967149026, "version": 2, "versionNonce": 939059582, "isDeleted": true, "boundElements": null, "updated": 1713723615080, "link": null, "locked": false, "text": "", "rawText": "", "fontSize": 20, "fontFamily": 4, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "", "lineHeight": 1.2 } ], "appState": { "theme": "dark", "viewBackgroundColor": "#ffffff", "currentItemStrokeColor": "#1e1e1e", "currentItemBackgroundColor": "transparent", "currentItemFillStyle": "solid", "currentItemStrokeWidth": 2, "currentItemStrokeStyle": "solid", "currentItemRoughness": 1, "currentItemOpacity": 100, "currentItemFontFamily": 4, "currentItemFontSize": 20, "currentItemTextAlign": "left", "currentItemStartArrowhead": null, "currentItemEndArrowhead": "arrow", "scrollX": 583.2388916015625, "scrollY": 573.6323852539062, "zoom": { "value": 1 }, "currentItemRoundness": "round", "gridSize": null, "gridColor": { "Bold": "#C9C9C9FF", "Regular": "#EDEDEDFF" }, "currentStrokeOptions": null, "previousGridSize": null, "frameRendering": { "enabled": true, "clip": true, "name": true, "outline": true } }, "files": {} } ``` %% [^1]: Clifford, J. (2025). *Deploying the Loki Helm on AWS | Grafana*. https://www.youtube.com/watch?v=5lXmWmofqwM