DevOps: Jenkins & AWS Series, Part 3: Jenkins Notifications and Gmail Integration:- A Step-by-Step Guide

Varun Kumar Manik
8 min readApr 15, 2023

--

DevOps Centralize Notification System

Jenkins Notifications and Gmail Integration

Introduction

In this third part of our DevOps: Jenkins & AWS series, we will discuss how to integrate Jenkins with Gmail for sending email notifications. This step-by-step guide will cover the

  • Key components of Jenkins notifications,
  • How to create a Gmail App password, and
  • How to integrate Gmail and Jenkins in the Jenkins configuration.
  • Finally, we will show you how to create and test a pipeline to send email notifications.

Jenkins is an open-source automation server that helps automate various parts of the software development process. One of the most important aspects of Jenkins is its ability to send notifications to keep team members informed about the status of builds and deployments. In this blog post, we’ll walk you through setting up Jenkins notifications with Gmail integration, enabling your team to stay up-to-date on their projects with ease.

Table of Contents:

  1. Jenkins Notification Key Components
  2. Prerequisites
  3. Configuring Gmail SMTP Settings
  4. Setting up Jenkins Email Notifications
  5. Creating a Jenkins Pipeline to Send Emails
  6. Testing Your Email Notifications
  7. Troubleshooting and Tips
  8. Conclusion

Jenkins Notification Key Components

In the context of Jenkins notifications, “trigger”, “content”, and “recipient” are three key components that define how and when email notifications are sent, what information they contain, and who receives them. Here’s an explanation of each component:

Trigger:

A trigger is an event or condition that, when met, initiates the sending of a Jenkins notification. Triggers are typically based on the status of a build or job. Common triggers include:

  • Build success: Sends a notification when a build or job completes successfully.
  • Build failure: Sends a notification when a build or job fails.
  • Build unstable: Sends a notification when a build or job is unstable (e.g., when there are test failures or other issues).
  • Build fixed: Sends a notification when a previously failed build or job is fixed (i.e., it becomes successful again).
  • Build started: Sends a notification when a build or job starts.

In a Jenkins pipeline, triggers can be defined within the post section, which is executed after the completion of the pipeline stages

Content:

The content of a Jenkins notification refers to the information included in the notification message. This typically contains details about the build or job, such as the job name, build number, build status, and any relevant log messages. The content can be customized using variables and scripting to meet specific requirements.

For example, in the emailext step shown in the previous answer, the subject and body parameters define the content of the email notification:

emailext (
to: 'your@email.com',
subject: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
body: "The job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' completed successfully."
)

Recipient:

The recipient is the individual or group of individuals who receive the Jenkins notification. In the context of email notifications, the recipient is defined by an email address or a list of email addresses.

In the emailext step, the to parameter defines the recipient(s) of the email notification:

emailext (
to: 'your@email.com',
subject: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
body: "The job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' completed successfully."
)

You can specify multiple recipients by separating their email addresses with a comma:

emailext (
to: 'user1@email.com, user2@email.com',
subject: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
body: "The job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' completed successfully."
)

By configuring the triggers, content, and recipients for Jenkins notifications, you can ensure that your team stays informed about the status of builds and deployments in real-time.

Prerequisites

Before we begin, make sure you have the following:

Configuring Gmail SMTP Settings

To integrate Gmail with Jenkins, we’ll need to configure Gmail’s SMTP settings.

  • Login to your Gmail and click on below link:

Create & use App Passwords

  • If you use 2-Step-Verification and get a “password incorrect” error when you sign in, you can try to use an App Password.
  • Go to your Google Account.
  • Select Security.
  • Under “Signing in to Google,” select App Passwords. You may need to sign in. If you don’t have this option, it might be because:
  • 2-Step Verification is not set up for your account.
  • 2-Step Verification is only set up for security keys.
  • Your account is through work, school, or other organization.
  • You turned on Advanced Protection.
  • After 2-Step Verification done it is look like following image
  • At the bottom, choose Select app and choose the app you using
  • Select device and choose the device you’re using

NOTE: If you can not find the app location click on above link.

  • Click on Generate.
  • Follow the instructions to enter the App Password. The App Password is the 16-character code in the yellow bar on your device.
  • Tap Done.

Setting up Jenkins Email Notifications

Now that we’ve configured Gmail’s SMTP settings, let’s set up Jenkins to send email notifications.

a. Log in to your Jenkins instance and navigate to “Manage Jenkins” > “Configure System.”

http://your_ip:8080/manage/configure

b. Scroll down to the “Extended E-mail Notification” section.

c. Enter the following SMTP settings:

  • SMTP server: smtp.gmail.com
  • Default user e-mail suffix: @gmail.com (replace with your own domain if using Google Workspace)
  • Use SMTP Authentication: Yes
  • User Name: Your full Gmail email address
  • Password: Your 16 digit app passowrd which you ahve genrated above
  • d. Click “Advanced” and check “Use SSL.”
  • e. Set the SMTP Port to 465.
  • f. Save the configuration.
  • Test Email

Creating a Jenkins Pipeline to Send Emails

Now that we’ve set up the email configuration, let’s create a Jenkins pipeline to send email notifications.

a. Create a new pipeline job or modify an existing one.

b. In the pipeline script section, add the following code:

pipeline {
agent any

stages {
stage('Build') {
steps {
// Your build steps here
}
}
}
post {
success {
emailext (
to: 'your@email.com',
subject: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
body: "The job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' completed successfully."
)
}
failure {
emailext (
to: 'your@email.com',
subject: "FAILURE: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
body: "The job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' failed."
)
}
}
}

Note: Replace ‘your@email.com’ with the desired recipient’s email address.

Testing Your Email Notifications

Now that we’ve set up our pipeline, let’s test it:

  • Run your pipeline job and verify that you receive email notifications for both success and failure scenarios.

Troubleshooting and Tips

If you’re not receiving email notifications, consider the following:

  • Double-check your Gmail SMTP settings and Jenkins email configuration.
  • Check your spam folder

Common Error:

java.net.SocketTimeoutException: connect timed out
at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:412)
at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:255)
at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:237)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.base/java.net.Socket.connect(Socket.java:609)
at com.sun.mail.util.SocketFetcher.createSocket(SocketFetcher.java:333)
at com.sun.mail.util.SocketFetcher.getSocket(SocketFetcher.java:214)
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2160)
Caused: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.gmail.com, 65; timeout 60000;
nested exception is:
java.net.SocketTimeoutException: connect timed out
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2210)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:722)
at jakarta.mail.Service.connect(Service.java:364)
at jakarta.mail.Service.connect(Service.java:222)
at jakarta.mail.Service.connect(Service.java:171)
at jakarta.mail.Transport.send0(Transport.java:230)
at jakarta.mail.Transport.send(Transport.java:100)
at hudson.tasks.Mailer$DescriptorImpl.doSendTestMail(Mailer.java:714)
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:710)
at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:397)
at org.kohsuke.stapler.Function$InstanceFunction.invoke(Function.java:409)
at org.kohsuke.stapler.interceptor.RequirePOST$Processor.invoke(RequirePOST.java:78)
at org.kohsuke.stapler.PreInvokeInterceptedFunction.invoke(PreInvokeInterceptedFunction.java:26)
at org.kohsuke.stapler.Function.bindAndInvoke(Function.java:207)
at org.kohsuke.stapler.Function.bindAndInvokeAndServeResponse(Function.java:140)
at org.kohsuke.stapler.MetaClass$11.doDispatch(MetaClass.java:558)
at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:770)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:900)
at org.kohsuke.stapler.MetaClass$4.doDispatch(MetaClass.java:289)
at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:59)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:770)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:900)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:836)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:900)
at org.kohsuke.stapler.MetaClass$9.dispatch(MetaClass.java:475)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:770)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:900)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:698)
at org.kohsuke.stapler.Stapler.service(Stapler.java:248)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:157)
at org.jenkinsci.plugins.ssegateway.Endpoint$SSEListenChannelFilter.doFilter(Endpoint.java:248)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
at jenkins.telemetry.impl.UserLanguages$AcceptLanguageFilter.doFilter(UserLanguages.java:129)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
at jenkins.security.ResourceDomainFilter.doFilter(ResourceDomainFilter.java:81)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
at io.jenkins.blueocean.ResourceCacheControl.doFilter(ResourceCacheControl.java:134)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
at io.jenkins.blueocean.auth.jwt.impl.JwtAuthenticationFilter.doFilter(JwtAuthenticationFilter.java:60)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:154)
at hudson.util.PluginServletFilter.doFilter(PluginServletFilter.java:160)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at hudson.security.csrf.CrumbFilter.doFilter(CrumbFilter.java:154)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:94)
at jenkins.security.AcegiSecurityExceptionFilter.doFilter(AcegiSecurityExceptionFilter.java:52)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at hudson.security.UnwrapSecurityExceptionFilter.doFilter(UnwrapSecurityExceptionFilter.java:54)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:110)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at jenkins.security.BasicHeaderProcessor.doFilter(BasicHeaderProcessor.java:97)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at hudson.security.HttpSessionContextIntegrationFilter2.doFilter(HttpSessionContextIntegrationFilter2.java:63)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:99)
at hudson.security.ChainedServletFilter.doFilter(ChainedServletFilter.java:111)
at hudson.security.HudsonFilter.doFilter(HudsonFilter.java:172)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at org.kohsuke.stapler.compression.CompressionFilter.doFilter(CompressionFilter.java:53)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at hudson.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:86)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at org.kohsuke.stapler.DiagnosticThreadNameFilter.doFilter(DiagnosticThreadNameFilter.java:30)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at jenkins.security.SuspiciousRequestFilter.doFilter(SuspiciousRequestFilter.java:38)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:549)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1570)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1383)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1543)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1305)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:563)
at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:934)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1078)
at java.base/java.lang.Thread.run(Thread.java:829)

Conclusion

In this third part of our DevOps: Jenkins & AWS series, we have demonstrated how to integrate Jenkins with Gmail for sending email notifications. We covered the key components of Jenkins notifications, including triggers, content, and recipients. We also walked you through the process of creating a Gmail App password and configuring Jenkins to use Gmail’s SMTP server for sending email notifications.

By following this step-by-step guide, you have successfully set up Gmail integration with Jenkins, allowing you to send email notifications to your team members about the status of builds and deployments. This integration enables better communication and collaboration within your team, ensuring that everyone stays up-to-date on the progress of your projects.

As you continue to expand your DevOps practices and toolchain, consider exploring other integrations, such as using Amazon SNS, Slack, or Microsoft Teams for notifications. These additional channels can help further enhance your team’s awareness and response time to build and deployment events, ultimately improving the efficiency and effectiveness of your development and operations processes.

For more info please connect & Follow me:

Github: https://github.com/manikcloud

LinkedIn: https://www.linkedin.com/in/vkmanik/

Email: varunmanik1@gmail.com

Facebook: https://www.facebook.com/cloudvirtualization/

YouTube: https://bit.ly/32fknRN

Twitter: https://twitter.com/varunkmanik

--

--

Varun Kumar Manik

AWS APN Ambassador | SME of DevOps DevSecOps | Cloud Architect & Trainer | Blogger | Youtuber |Chef