{"id":31565441,"url":"https://github.com/filippofinke/docker-events","last_synced_at":"2026-04-28T21:34:49.841Z","repository":{"id":317388422,"uuid":"1067202941","full_name":"filippofinke/docker-events","owner":"filippofinke","description":"🐋 Real-time docker events notifications","archived":false,"fork":false,"pushed_at":"2025-09-30T14:20:26.000Z","size":39,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-08T23:39:06.954Z","etag":null,"topics":["discord","docker","docker-events","events","notifications","real-time","slack","status","stream","uptime"],"latest_commit_sha":null,"homepage":"https://github.com/filippofinke/docker-events","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/filippofinke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-30T14:16:55.000Z","updated_at":"2025-09-30T14:20:59.000Z","dependencies_parsed_at":"2025-09-30T16:24:24.671Z","dependency_job_id":"9aecd77f-194e-4ff9-a0e2-edd91fa38d8e","html_url":"https://github.com/filippofinke/docker-events","commit_stats":null,"previous_names":["filippofinke/docker-events"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/filippofinke/docker-events","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filippofinke%2Fdocker-events","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filippofinke%2Fdocker-events/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filippofinke%2Fdocker-events/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filippofinke%2Fdocker-events/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/filippofinke","download_url":"https://codeload.github.com/filippofinke/docker-events/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/filippofinke%2Fdocker-events/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32400867,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["discord","docker","docker-events","events","notifications","real-time","slack","status","stream","uptime"],"created_at":"2025-10-05T07:07:03.264Z","updated_at":"2026-04-28T21:34:49.835Z","avatar_url":"https://github.com/filippofinke.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/filippofinke/docker-events\"\u003e\n    \u003cimg width=\"200px\" src=\"https://github.com/user-attachments/assets/e9712d6a-32e9-4e9b-a545-11aa88dadf65\" alt=\"Docker Events\" /\u003e\n  \u003c/a\u003e\n  \u003ch3 align=\"center\"\u003eDocker Events\u003c/h3\u003e\n\u003c/div\u003e\n\n\u003e Watch Docker events in real-time and dispatch rich notifications with a lightweight Go service.\n\nThis project uses docker system events and forwards meaningful summaries through configurable notification channels powered by [`nikoksr/notify`](https://github.com/nikoksr/notify). It is designed to be small, dependable, and easy to extend with new transports or event processing rules.\n\n## Features\n\n- [x] Real-time streaming of `docker system events` via a managed watcher\n- [x] Human-friendly notifications enriched with context (timestamp, actor attributes, status)\n- [x] Multi-channel delivery (Slack, Telegram, Discord) powered by `github.com/nikoksr/notify`\n- [x] Opt-in filtering by Docker event types and CLI filters\n- [x] Environment variable driven configuration with validation at startup\n- [x] Composable code structure (config, watcher, notifier) and unit tests for formatting helpers\n\n## Quick Start\n\nPrerequisites\n\n- Go 1.24+\n- Docker CLI with access to the target daemon\n- At least one notifications provider configured (Slack bot token + channel IDs, Telegram bot token + chat IDs, Discord bot token + channel IDs, or Discord webhook URLs)\n\nClone \u0026 install dependencies\n\n```bash\ngit clone https://github.com/filippofinke/docker-events.git\ncd docker-events\ngo mod tidy\n```\n\nConfigure environment\n\n1. Copy the example environment file:\n\n```bash\ncp .env.example .env\n```\n\n2. Update `.env` with your Slack token, target channels, and any optional Docker filters.\n\nRun locally\n\n```bash\n# start the watcher (module-aware path)\ngo run ./cmd\n```\n\nThe service will stream logs to stdout and post notifications for matching Docker events. Stop with `Ctrl+C`.\n\n## Configuration\n\nAll settings are provided via environment variables (see `.env.example`). Key options:\n\n- `SLACK_BOT_TOKEN`: Slack bot token used to authenticate the notifier.\n- `SLACK_CHANNEL_IDS`: Comma-separated list of Slack channel IDs (e.g. `C0123456,C0ABCDEF`).\n- `TELEGRAM_BOT_TOKEN`: Telegram bot token created with [BotFather](https://core.telegram.org/bots#6-botfather).\n- `TELEGRAM_CHAT_IDS`: Comma-separated list of chat IDs (negative values for group chats are supported).\n- `DISCORD_BOT_TOKEN`: Discord bot token generated from the Developer Portal.\n- `DISCORD_CHANNEL_IDS`: Comma-separated list of Discord channel IDs to notify.\n- `DISCORD_WEBHOOK_URLS`: Comma-separated list of Discord webhook URLs (recommended over bot tokens for simple notifications).\n- `NOTIFY_SUBJECT_PREFIX`: Prefix for notification subjects (defaults to `Docker event`).\n- `MESSAGE_TEMPLATE`: Custom message template using Go template syntax (see [Message Customization](#message-customization) below).\n- `MESSAGE_LOG_LINES`: Number of container log lines to fetch for events (defaults to 0, disabled).\n- `EVENT_GROUP_WINDOW`: Time window for grouping events from the same container (e.g., `5s`, `10s`, `1m`). Events for the same container within this window will be grouped into a single notification. Set to `0` to disable grouping (defaults to `5s`).\n- `DOCKER_EVENT_FILTERS`: Comma-separated filters passed to `docker system events` (same syntax as the CLI `--filter` flag, e.g. `status=start,type=container`).\n- `DOCKER_EVENT_TYPES`: Comma-separated list of Docker event types to keep (e.g. `container,image,volume`).\n\n\u003e **Security note:** Do not commit an `.env` file containing real tokens. Use `.env` locally or provide the variables through your orchestrator of choice.\n\n## Docker Event Filters\n\nThe Docker CLI supports a rich set of filters that can be combined in `DOCKER_EVENT_FILTERS`. Supported filter keys include:\n\n- `config=\u003cname or id\u003e`\n- `container=\u003cname or id\u003e`\n- `daemon=\u003cname or id\u003e`\n- `event=\u003cevent action\u003e`\n- `image=\u003crepository or tag\u003e`\n- `label=\u003ckey\u003e` or `label=\u003ckey\u003e=\u003cvalue\u003e`\n- `network=\u003cname or id\u003e`\n- `node=\u003cid\u003e`\n- `plugin=\u003cname or id\u003e`\n- `scope=\u003clocal or swarm\u003e`\n- `secret=\u003cname or id\u003e`\n- `service=\u003cname or id\u003e`\n- `type=\u003ccontainer|image|volume|network|daemon|plugin|service|node|secret|config\u003e`\n- `volume=\u003cname\u003e`\n\nProvide multiple filters by comma-separating entries (e.g. `DOCKER_EVENT_FILTERS=event=start,scope=swarm`); the service will translate each entry into an individual `--filter` flag for `docker system events`.\n\nMore details in the [Docker documentation](https://docs.docker.com/reference/cli/docker/system/events/#filter).\n\n## Docker Event Types\n\n`DOCKER_EVENT_TYPES` narrows processing to one or more top-level Docker object kinds. Valid values:\n\n- `container`\n- `image`\n- `plugin`\n- `volume`\n- `network`\n- `daemon`\n- `service`\n- `node`\n- `secret`\n- `config`\n\nLeave the variable empty to accept every event type from the stream.\n\nMore details in the [Docker documentation](https://docs.docker.com/engine/reference/commandline/system_events/#object-types).\n\n## Event Grouping\n\nTo prevent notification spam when a container experiences multiple events in quick succession (e.g., during a restart: kill → stop → die → start → restart), docker-events can group events for the same container within a configurable time window.\n\n### How It Works\n\nWhen event grouping is enabled (default: 5 seconds), the service will:\n\n1. Collect all events for the same container within the time window\n2. Wait for the window to expire or for events from a different container\n3. Send a single grouped notification with all events instead of individual messages\n\n### Configuration\n\nSet the `EVENT_GROUP_WINDOW` environment variable to control the grouping window:\n\n```bash\nEVENT_GROUP_WINDOW=5s   # Default: group events within 5 seconds\nEVENT_GROUP_WINDOW=10s  # Group events within 10 seconds\nEVENT_GROUP_WINDOW=1m   # Group events within 1 minute\nEVENT_GROUP_WINDOW=0    # Disable grouping, send all events immediately\n```\n\nValid time units: `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`\n\n### Grouped Notification Format\n\nWhen multiple events are grouped, the notification will include:\n\n- Container ID and total event count\n- Time range (first to last event)\n- Common attributes shared across all events\n- List of all events with timestamps and actions\n\nExample grouped notification:\n\n```\nDocker events: 5 events for container d23c731f32ba (die, kill, restart, start, stop)\n\nContainer: d23c731f32ba41defa48b2804299e9378b84442857701b1d51b8e6aca77c35da\nEvent count: 5\nTime range: 2025-10-09T08:10:58Z to 2025-10-09T08:10:59Z\n\nCommon attributes:\n  - com.docker.compose.project=myapp\n  - com.docker.compose.service=web\n  - image=nginx:latest\n\nEvents:\n  1. [08:10:58] container kill\n  2. [08:10:59] container stop\n  3. [08:10:59] container die\n  4. [08:10:59] container start\n  5. [08:10:59] container restart\n```\n\n### Benefits\n\n- **Reduced notification spam**: Restart operations typically generate 5+ events, which are now grouped into one message\n- **Better context**: See all related events together with their timing\n- **Cleaner notification channels**: Fewer messages to scroll through\n- **Preserved details**: All event information is retained, just organized better\n\n### Behavior Notes\n\n- Single events are sent immediately (no grouping overhead)\n- Events for different containers are never grouped together\n- The timer resets each time a new event arrives for the same container\n- On shutdown, all pending grouped events are flushed immediately\n- **Custom templates are fully supported**: If you have a `MESSAGE_TEMPLATE` configured, it will be used for both single and grouped events. Use `{{.EventCount}}` and `{{.Events}}` in your template to handle grouped events differently.\n\n## Message Customization\n\nBy default, docker-events sends detailed notifications with all available event information. You can customize the message format using Go templates via the `MESSAGE_TEMPLATE` environment variable.\n\n### Available Template Placeholders\n\n- `{{.Type}}` - Event type (container, image, volume, network, etc.)\n- `{{.Action}}` - Event action (start, stop, create, destroy, etc.)\n- `{{.ID}}` - Full object ID\n- `{{.ShortID}}` - Short ID (first 12 characters)\n- `{{.Name}}` - Container/object name (extracted from attributes when available)\n- `{{.Status}}` - Event status\n- `{{.From}}` - From field (typically the image name for container events)\n- `{{.Time}}` - Event timestamp in RFC3339 format\n- `{{.Scope}}` - Event scope (local or swarm)\n- `{{.Actor.ID}}` - Actor ID\n- `{{.Attribute \"key\"}}` - Get specific attribute value by key\n- `{{.GetLogs}}` - Fetch container logs (requires `MESSAGE_LOG_LINES` \u003e 0)\n- `{{.EventCount}}` - Number of events (returns 1 for single events, \u003e1 for grouped events)\n- `{{.Events}}` - Array of all events (only available for grouped events; use with range)\n\n**Note:** When events are grouped (multiple events for the same container), `{{.Type}}`, `{{.Action}}`, etc. refer to the first event in the group. Use `{{.Events}}` to access all events in the group.\n\n### Template Examples\n\n**Simple notification:**\n\n```bash\nMESSAGE_TEMPLATE=\"Container {{.Name}} ({{.ShortID}}) {{.Action}} at {{.Time}}\"\n```\n\n**With container logs:**\n\n```bash\nMESSAGE_TEMPLATE=\"Container {{.Name}} {{.Action}}\\nImage: {{.From}}\\nLogs:\\n{{.GetLogs}}\"\nMESSAGE_LOG_LINES=20\n```\n\n**Custom attributes:**\n\n```bash\nMESSAGE_TEMPLATE=\"{{.Type}} {{.Action}}: {{.Name}}\\nProject: {{.Attribute \\\"com.docker.compose.project\\\"}}\\nService: {{.Attribute \\\"com.docker.compose.service\\\"}}\"\n```\n\n**Conditional formatting:**\n\n```bash\nMESSAGE_TEMPLATE=\"{{.Type}} {{.Action}}: {{if .Name}}{{.Name}}{{else}}{{.ShortID}}{{end}}\\nTime: {{.Time}}\"\n```\n\n**Grouped events with custom template:**\n\n```bash\nMESSAGE_TEMPLATE=\"{{if gt .EventCount 1}}🔄 {{.EventCount}} events for {{.Name}} ({{.ShortID}})\\n{{range .Events}}- [{{.Timestamp.Format \\\"15:04:05\\\"}}] {{.Action}}\\n{{end}}{{else}}{{.Type}} {{.Action}}: {{.Name}}\\nTime: {{.Time}}{{end}}\"\nEVENT_GROUP_WINDOW=5s\n```\n\n**Grouped events with logs:**\n\n```bash\nMESSAGE_TEMPLATE=\"Container: {{.Name}} ({{.ShortID}})\\nEvents: {{.EventCount}}\\n{{if gt .EventCount 1}}Actions: {{range .Events}}{{.Action}} {{end}}\\n{{end}}{{if .GetLogs}}\\nLogs:\\n{{.GetLogs}}{{end}}\"\nMESSAGE_LOG_LINES=20\nEVENT_GROUP_WINDOW=5s\n```\n\n### Logs Configuration\n\nSet `MESSAGE_LOG_LINES` to fetch the last N lines of container logs when using `{{.GetLogs}}`:\n\n```bash\nMESSAGE_LOG_LINES=10  # Fetch last 10 lines\nMESSAGE_LOG_LINES=50  # Fetch last 50 lines\nMESSAGE_LOG_LINES=0   # Disable log fetching (default)\n```\n\n**Note:** Log fetching only works for container events and may add latency to notifications. Use reasonable line counts to avoid performance issues.\n\n### Default Template\n\nIf no custom template is provided, the default format includes:\n\n```\nTime: \u003ctimestamp\u003e\nStatus: \u003cstatus\u003e\nFrom: \u003cfrom\u003e\nScope: \u003cscope\u003e\nID: \u003cid\u003e\nActor: \u003cactor_id\u003e\n```\n\n## Discord Webhooks vs Bots\n\nThis project supports two methods for Discord notifications:\n\n**Discord Webhooks (Recommended)**\n\nDiscord webhooks are the simplest way to send notifications. They use HTTP POST requests and don't require a bot session or gateway connection.\n\nAdvantages:\n\n- Simpler setup - just create a webhook in your Discord channel settings\n- No bot permissions or OAuth scopes needed\n- More efficient - uses plain HTTP instead of maintaining a WebSocket connection\n- Can send to multiple channels by providing multiple webhook URLs\n\nTo create a Discord webhook:\n\n1. Open your Discord server and go to the channel where you want notifications\n2. Click the gear icon (Edit Channel) next to the channel name\n3. Go to \"Integrations\" → \"Webhooks\" → \"New Webhook\"\n4. Copy the webhook URL and add it to `DISCORD_WEBHOOK_URLS`\n\nExample:\n\n```bash\nDISCORD_WEBHOOK_URLS=https://discord.com/api/webhooks/123456789/your-webhook-token\n```\n\n**Discord Bot (Alternative)**\n\nBot tokens can be used if you need more advanced features or already have a bot infrastructure. Configure with `DISCORD_BOT_TOKEN` and `DISCORD_CHANNEL_IDS`.\n\nYou can also use both webhooks and bot tokens simultaneously if needed.\n\n## Extending Notifications\n\n`internal/notifier` wraps `github.com/nikoksr/notify`, so adding more destinations is straightforward:\n\n1. Import the desired service package (e.g. `github.com/nikoksr/notify/service/telegram`).\n2. Create a service instance in `Setup` based on new configuration.\n3. Register it with the shared notifier (`n.client.UseServices(service)`).\n\n## Running Tests\n\n```bash\ngo test ./...\n```\n\n## Docker Usage\n\nA minimal container image can be built with:\n\n```bash\n# build the Go binary locally and package it into a container image\ndocker build -t docker-events:latest .\n```\n\nThe included `Dockerfile` uses a multi-stage build: it compiles a static Go binary in a Go builder image and copies it into the official Docker CLI image so the binary can call `docker` if needed.\n\nImportant runtime considerations:\n\n- The service talks to the Docker daemon. In most deployments you should mount the host Docker socket into the container so the service can observe events:\n\n  - `/var/run/docker.sock:/var/run/docker.sock:ro` (read-only mount used in the example compose file)\n\n- Environment variables are used for configuration. The repository contains a `.env.example`—copy it to `.env` and set your provider tokens and channels. Do not commit `.env` with real secrets.\n\nCompose example (loads `.env` automatically)\n\n```yaml\nservices:\n  docker-events:\n    build: .\n    env_file:\n      - .env\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n    restart: unless-stopped\n```\n\nStart with docker-compose:\n\n```bash\n# ensure .env exists in the project root (copy from .env.example)\ncp .env.example .env\n# build \u0026 start in background\ndocker compose up -d --build\n# view logs\ndocker compose logs -f docker-events\n```\n\nIf you prefer to run the image directly:\n\n```bash\ndocker run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro \\\n  --env-file .env filippofinke/docker-events:latest\n```\n\nRemember: by default Docker Compose loads a top-level `.env` file. The `env_file` entry above is explicit and can be used by other tools that also support `env_file`.\n\n## Author\n\n👤 **Filippo Finke**\n\n- Website: [https://filippofinke.ch](https://filippofinke.ch)\n- Twitter: [@filippofinke](https://twitter.com/filippofinke)\n- GitHub: [@filippofinke](https://github.com/filippofinke)\n- LinkedIn: [@filippofinke](https://linkedin.com/in/filippofinke)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffilippofinke%2Fdocker-events","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffilippofinke%2Fdocker-events","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffilippofinke%2Fdocker-events/lists"}