<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mahin Rahman</title>
    <description>The latest articles on Forem by Mahin Rahman (@mahinmunx).</description>
    <link>https://forem.com/mahinmunx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3816720%2Ff30902d1-ae1c-4cf5-b35c-ce80f5cdf260.jpg</url>
      <title>Forem: Mahin Rahman</title>
      <link>https://forem.com/mahinmunx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mahinmunx"/>
    <language>en</language>
    <item>
      <title>Automate Your Grafana Reporting: Introducing GrafMail</title>
      <dc:creator>Mahin Rahman</dc:creator>
      <pubDate>Tue, 10 Mar 2026 11:13:12 +0000</pubDate>
      <link>https://forem.com/mahinmunx/automate-your-grafana-reporting-introducing-grafmail-377b</link>
      <guid>https://forem.com/mahinmunx/automate-your-grafana-reporting-introducing-grafmail-377b</guid>
      <description>&lt;p&gt;&lt;strong&gt;Stop taking manual screenshots. Start automating your DevOps monitoring reports with a secure, Dockerized solution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's Monday morning. Your stakeholders need a summary of last week's system performance. You open Grafana, adjust the time range, hide the sidebar, take a screenshot, paste it into an email, and repeat the process for five different dashboards.&lt;/p&gt;

&lt;p&gt;It's tedious, error-prone, and frankly, a waste of valuable engineering time.&lt;/p&gt;

&lt;p&gt;If you're looking to automate this workflow without sacrificing security or flexibility, meet &lt;strong&gt;GrafMail&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;GrafMail is an automated Grafana dashboard screenshot and delivery tool designed for DevOps teams. It captures PNG or PDF reports using a headless browser inside Docker and delivers them via SMTP email or SFTP. Whether you are running in a cloud-native environment or a strictly air-gapped on-prem setup, GrafMail has you covered.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why GrafMail?
&lt;/h2&gt;

&lt;p&gt;Existing screenshot tools often lack the flexibility required for enterprise environments. GrafMail was built to solve specific pain points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Dual Delivery Modes:&lt;/strong&gt; Send reports directly via email (&lt;code&gt;SMTP_INTERNAL&lt;/code&gt;) or save them for external processing (&lt;code&gt;FILE_ONLY&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Professional Formatting:&lt;/strong&gt; HTML emails with inline screenshots (Content-ID) or merged PDF attachments.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Docker-First:&lt;/strong&gt; Runs in an isolated container with configurable resource limits (CPU/Memory).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Air-Gapped Ready:&lt;/strong&gt; Supports offline deployment via Docker image tarballs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Security Focused:&lt;/strong&gt; Runs as a non-root user, supports SSH key authentication, and avoids committing secrets to Git.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;At its core, GrafMail is a Python orchestration engine powered by &lt;strong&gt;Playwright&lt;/strong&gt; and &lt;strong&gt;Chromium&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Configuration:&lt;/strong&gt; You define your Grafana URL, credentials, and delivery preferences in a simple &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Capture:&lt;/strong&gt; The Docker container launches Chromium, logs into Grafana, and captures the dashboard viewport or specific panels.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Processing:&lt;/strong&gt; Images can be merged into a single PDF report automatically.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Delivery:&lt;/strong&gt; Depending on your mode, the tool either emails the report via SMTP or copies it to a remote server via SFTP (Paramiko).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dtn92z5ic8yk2tk63w8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dtn92z5ic8yk2tk63w8.png" alt="GrafMail Architecture Overview" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(Above: A simplified view of the GrafMail workflow. The &lt;code&gt;run_report.sh&lt;/code&gt; script orchestrates the Docker container, which handles capture and delivery.)&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Flexible Delivery Modes
&lt;/h3&gt;

&lt;p&gt;You aren't locked into one method of distribution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SMTP_INTERNAL:&lt;/strong&gt; The container sends the email itself. Perfect for simple cron jobs. Supports CC/BCC and enforces a 10MB attachment limit to prevent bounce-backs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;FILE_ONLY:&lt;/strong&gt; Screenshots are saved to a local directory. You can optionally configure the tool to push these files to a remote server via SFTP using SSH keys or password authentication.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. PDF &amp;amp; Panel Support
&lt;/h3&gt;

&lt;p&gt;Need a single document for management? Set &lt;code&gt;SCREENSHOT_FORMAT=pdf&lt;/code&gt;. GrafMail captures individual panels and merges them into a single PDF report automatically. You can also target specific panel IDs instead of capturing the entire viewport.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Air-Gapped Deployment
&lt;/h3&gt;

&lt;p&gt;Working in a secure environment without internet access? GrafMail supports a two-stage deployment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Build and save the Docker image (&lt;code&gt;docker save&lt;/code&gt;) on a connected machine.&lt;/li&gt;
&lt;li&gt; Transfer the &lt;code&gt;.tar&lt;/code&gt; file and run &lt;code&gt;./run_report.sh --load-tar grafmail.tar&lt;/code&gt; on the secure machine.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  4. Robust Error Handling
&lt;/h3&gt;

&lt;p&gt;Network blips happen. GrafMail includes built-in retry logic (&lt;code&gt;CAPTURE_RETRY_COUNT&lt;/code&gt;), configurable timeouts, and spinners detection to ensure dashboards are fully loaded before capture.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;p&gt;Getting started takes less than 5 minutes.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Configure
&lt;/h3&gt;

&lt;p&gt;Copy the example environment file and fill in your Grafana details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit .env with your GRAFANA_URL, UID, and SMTP details&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Build
&lt;/h3&gt;

&lt;p&gt;Build the optimized Docker image (Python 3.11-slim + Chromium):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; grafmail:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Run
&lt;/h3&gt;

&lt;p&gt;Execute the runner script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./run_report.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Automate
&lt;/h3&gt;

&lt;p&gt;Add it to your crontab for regular reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Every 30 minutes&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/30 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /path/to/run_report.sh &lt;span class="nt"&gt;--env-file&lt;/span&gt; /path/to/.env &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /path/to/cron.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;p&gt;In DevOps, automation shouldn't come at the cost of security. GrafMail follows strict guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Secret Management:&lt;/strong&gt; Never commit &lt;code&gt;.env&lt;/code&gt; to Git. Use environment variables or mounted secrets.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSH Hardening:&lt;/strong&gt; When using remote copy, prefer SSH keys over passwords. Set &lt;code&gt;SSH_HOST_KEY_POLICY=reject&lt;/code&gt; in production to prevent man-in-the-middle attacks.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;TLS Enforcement:&lt;/strong&gt; Use HTTPS for Grafana and TLS/SSL for SMTP connections.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Advanced Configuration
&lt;/h2&gt;

&lt;p&gt;GrafMail is highly configurable via environment variables. Here are a few powerful options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GRAFANA_DASHBOARD_UID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The unique ID from your dashboard URL.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELIVERY_MODE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Choose &lt;code&gt;SMTP_INTERNAL&lt;/code&gt; or &lt;code&gt;FILE_ONLY&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DOCKER_MEMORY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limit container memory (e.g., &lt;code&gt;1g&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FILE_RETENTION_DAYS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-cleanup old reports (e.g., &lt;code&gt;7&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NO_IMAGES_ACTION&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decide what happens if capture fails (&lt;code&gt;notify&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt;, or &lt;code&gt;fail&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a full list of configuration options, check the &lt;a href="https://mahinxander.github.io/Grafmail-Grafana-Screenshot-Tool/" rel="noopener noreferrer"&gt;Configuration Reference&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting Common Issues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SMTP Connection Refused:&lt;/strong&gt; Check your firewall rules and ensure &lt;code&gt;SMTP_HOST&lt;/code&gt; and &lt;code&gt;SMTP_PORT&lt;/code&gt; are correct. Enable &lt;code&gt;DEBUG_MODE=true&lt;/code&gt; for verbose logs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Container OOM Killed:&lt;/strong&gt; Rendering dashboards can be memory-intensive. Increase &lt;code&gt;DOCKER_MEMORY&lt;/code&gt; in your &lt;code&gt;.env&lt;/code&gt; file (e.g., to &lt;code&gt;2g&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SSH Host Key Errors:&lt;/strong&gt; If using &lt;code&gt;FILE_ONLY&lt;/code&gt; with Paramiko, ensure your &lt;code&gt;known_hosts&lt;/code&gt; file is mounted or set &lt;code&gt;SSH_HOST_KEY_POLICY=warn&lt;/code&gt; for development.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Manual reporting is a relic of the past. With GrafMail, you can ensure your stakeholders receive consistent, high-quality monitoring reports without lifting a finger. It's lightweight, secure, and designed to fit into modern DevOps pipelines.&lt;/p&gt;

&lt;p&gt;Ready to automate your Grafana workflows?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;GitHub Repo:&lt;/strong&gt; &lt;a href="https://github.com/mahinxander/Grafmail" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://mahinxander.github.io/Grafmail-Grafana-Screenshot-Tool/" rel="noopener noreferrer"&gt;Read the Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;License:&lt;/strong&gt; MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Have you tried GrafMail? Share your feedback or contribute to the project on GitHub!&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #DevOps #Grafana #Automation #Python #Docker #Monitoring #SRE #OpenSource&lt;/p&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>monitoring</category>
      <category>github</category>
    </item>
  </channel>
</rss>
