Alloy process multiple docker logs

Hey there,

Im new to alloy, tried to setup promtail before but I read it‘s in maintenance mode and would rather start with alloy.

My config file is below.

What I like to achieve is to process each docker container with it‘s own regex, or maybe add multiple docker containers to the same regex match if their logging format is the same. I have 40 docker containers which all log in different formats.

Should i change the way i currently configured the discovery process? e.g. make multiple separat ones so i can choose to send them for processing or not?

Or can I leave it like it is and make a catch all one for any containers that do not meet the regex of the matched lines?

The homeassistant match also does not work, i currently dont get any logs passed to loki. If i send the logs without processing and remove that block i‘m getting all logs.

// ###############################
// #### Logging Configuration ####
// ###############################

// Discover Docker containers and extract metadata.
discovery.docker “linux” {
host = “unix:///var/run/docker.sock”
}

// Define a relabeling rule to create a service name from the container name.
discovery.relabel “logs_integrations_docker” {
targets =

  rule {
      source_labels = ["__meta_docker_container_name"]
      regex = "/(.*)"
      target_label = "container_name"
  }

 rule {
    target_label = "instance"
    replacement  = constants.hostname
}

}

// Configure a loki.source.docker component to collect logs from Docker containers.
loki.source.docker “default” {
host = “unix:///var/run/docker.sock”
targets = discovery.docker.linux.targets
relabel_rules = discovery.relabel.logs_integrations_docker.rules
forward_to = [loki.process.docker_logs.receiver]
// forward_to = [loki.write.local.receiver]
}

loki.process “docker_logs” {
stage.match {
selector = {container_name="homeassistant"}

stage.regex {
  expression = "^(?P<ts>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d+)\\s+(?P<level>[A-Z]+)\\s+\\((?P<thread>[^)]+)\\)\\s+\\[(?P<logger>[^\\]]+)\\]\\s+(?P<message>.*)$"
}

stage.timestamp {
  source   = "timestamp"
  format   = "2006-01-02 15:04:05.000"
  location = "Local"
}

}

stage.match {
selector = {container_name="nginx"}

stage.regex {
  expression = "^(?P<remote_addr>\\S+) - (?P<remote_user>\\S+) \\[(?P<time_local>[^\\]]+)\\] \"(?P<request>[^\"]+)\" (?P<status>\\d{3}) (?P<body_bytes_sent>\\d+) \"(?P<http_referer>[^\"]*)\" \"(?P<http_user_agent>[^\"]*)\""
}

stage.timestamp {
  source   = "time_local"
  format   = "02/Jan/2006:15:04:05 -0700"
  location = "Local"
}

}

forward_to = [loki.write.local.receiver]
}

loki.write “local” {
endpoint {
url = “http://loki:3100/loki/api/v1/push”
}
}

  1. If you remove the match block, do the logs that come in contain the container_name label?
  2. I believe loki.source.docker does the timestamp for you already.

My personal preference and recommendation to you is to keep log processing to a minimum. 40 pipelines for 40 containers is not only excessive, it’s also difficult to maintain. Much better to keep processing to a minimum and use Loki’s various query functions to parse the logs during query time instead. Process timestamps is a good practice, but i am pretty sure that’s taken care of for you already by loki.source.docker.

Hi Tony, thanks fpr the answer. Previously I was using Graylog with GROK patterns.

  1. yes, everything comes in just fine and the label is there.
  2. The timestamp, however, is still in the message and it also does not take the message timestamp, but it takes the ingestion timestamp.

it won‘t have 40 processing patterns and regexes. But I would expect my log messages to show the actual messages rather than something like this:

[2025-10-01 08:10:00,004] [INFO] [celery.worker.strategy] Task paperless_mail.tasks.process_mail_accounts[21770f65-9e76-414b-b672-8eafc25ffad7] received

In your timestamp stage, you have the wrong reference in a couple of places. Also, I am not sure if "Local" is actually an accepted location. For example:

stage.match {
  selector = {container_name="homeassistant"}

  stage.regex {
    expression = "^(?P<ts>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d+)\\s+(?P<level>[A-Z]+)\\s+\\((?P<thread>[^)]+)\\)\\s+\\[(?P<logger>[^\\]]+)\\]\\s+(?P<message>.*)$"
  }

  stage.timestamp {
    source   = "timestamp"  # should be "ts"
    format   = "2006-01-02 15:04:05.000"
    location = "Local" # may be not valid
  }
}

Hi Tony, thanks for pointing that out with the timestamp. However I changed my config a bit so the timestamp now works:

loki.process “container_processing” {
forward_to = [loki.write.local.receiver]

stage.match {
selector = {container_name=~"homeassistant|palworld-server"}

stage.decolorize {}

stage.regex {
  expression = "^(?P<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) (?P<level>\\w+) \\((?P<thread>[^)]+)\\) \\[(?P<component>[^\\]]+)\\] (?P<message>.*)$"
}

stage.timestamp {
  source = "timestamp"
  format = "2006-01-02 15:04:05.000"
  location = "Europe/Berlin"
}

stage.labels {
  values = {
    component = "component",
  }
}

}
}

The last question is how can i rewrite the current message? If I see performance degrade too much, i‘ll probably refrain from that. But I would like to try.

It’s a similar process, you would use stage.regex with group capture and parse for what you would want as the log body, then use stage.output to overwrite the body of the log (see loki.process | Grafana Alloy documentation)

Thank you!