Gitlab hero border pattern left svg Gitlab hero border pattern right svg

Blog Handbook

The GitLab blog is managed by @rebecca the Managing Editor.


Goal: Grow our audience, engage readers, and convert readers into subscribers

The GitLab blog exists for our audience and we serve our audience first. Without an audience, blog content cannot support our objectives. Every blog post published on the GitLab blog should be crafted with our audience needs and wants in mind. The GitLab blog is not self-serving; if content is purely promotional and adds no value to our readership, it will not be published.

We measure success by tracking the following metrics:

  1. Engagement: # of page views
  2. Reach: # of new visitors
  3. Conversion: # of subscribers both net new and total


  1. Build credibility and authority.
  2. Increase awareness of GitLab the company, product, and community.
  3. Increase organic search rankings and traffic.
  4. Contribute to lead generation and revenue.


Mission statement: Create, curate, and elevate stories that increase awareness of the benefits of a single application for the entire DevOps lifecycle, as well as awareness of GitLab as a pioneer in all-remote work.

Please see Attributes of a successful blog post below for examples of stories that perform well on our blog.

The blog is not the permanent place for tutorials, which should live in the docs and should be linked to when relevant.

Ideal content mix


  1. Technical: Articles which dive deep into a technical topic and provide technical details on how to use, implement, or solve something. Technical articles often include code snippets or screen shots, and provide tangible results or actions for the reader.
  2. Features: Articles which provide scope, depth, and interpretation of trends, events, topics, or people. Includes articles covering customer stories, trends and/or data analysis, and journalistic reporting on a topic, event, or person.
  3. Product: Articles that cover GitLab's product evolution, features, and capabilities. Includes release posts, integrations, feedback solicitations, and use case articles centered around how to use GitLab to do something.
  4. Remote work: Articles that cover all-remote work. Includes articles about work/life balance, tricks and tips, building an all-remote company, etc.
  5. Corporate news: Articles covering GitLab news and announcements such as:

Publishing process

If you'd like to write about something for the GitLab blog, please follow the instructions below to pitch your idea to the Editorial team. This process is intended to ensure the Editorial team is spending the bulk of their time working on posts that get results and align with the goals for the blog.

If you prefer, you can submit a complete blog post to the GitLab Unfiltered blog without pitching first. The Unfiltered blog is monitored weekly for posts to feature on the main blog. Unfiltered posts are not shared on GitLab's official social media handles.

Why do we use a pitching process? Many people throughout GitLab the company and community contribute to the blog. Our pitching process ensures we can prioritize submissions so we don't overwhelm our audience with too much content at once, and to help our contributors craft their ideas in a way that will get views. After reviewing years of data, we know what type of content is likely to get views and what is unlikely to resonate. We want to help every contributor maximize the impact of their contribution.

Data shows that more blog posts does not equal more views. In fact, too much content can have a negative impact on our traffic. The pitching process allows us to review each article and schedule it at the most appropriate time. We've found that publishing ~3 articles per week is the most effective publishing cadence for the GitLab blog.

How to pitch a blog post

Open an issue in the project, using the Blog post template. Include a short summary of your proposed blog post where indicated, and add the Blog::Pitch label to ensure your pitch is triaged.

If you hope to publish on a specific date or within a specific timeframe, please allow at least three weeks' lead time from the date you open your issue and ping @rebecca immediately, as the Editorial calendar fills up quickly and we cannot guarantee there will be a slot for your post without adequate notice. Please give a rationale for your proposed publish date in the issue description so the team can prioritize accordingly.

If you do not intend to write the post yourself but are just suggesting an idea you think would be worth pursuing, please make this clear in your issue description.

Note: If you're planning to write a tutorial post describing how to do something with GitLab, please ensure that the relevant documentation exists first before you write a blog post about it. A blog post should not replace documentation, but should add more information, context, and color around a technical subject, linking back to the documentation for full instructions.

Triage of pitches

The Editorial team will review issues with the label Blog::Pitch every Monday, and a team member will respond on your issue to let you know whether to move forward with your draft.

If your pitch isn't accepted, no problem! The Editorial team will suggest that you submit your post to the GitLab Unfiltered blog instead. The Unfiltered blog is monitored weekly for posts to feature on the main blog.

How to get your blog post pitch accepted

The team will be assessing pitches against the following criteria:

  1. The post idea aligns with the blog objectives and scope; and/or
  2. The idea shows potential to become a high-performing post

To give your idea the best chance of being accepted, be sure to demonstrate how your pitch meets the criteria above. It may also help to think about the following:

  1. Who is your post for? (i.e. Who is the intended audience?)
  2. What will readers gain from reading your post? What will they learn?
  3. Are there tangible examples/outcomes you can share to illustrate your points? Issues/MRs/epics you can link to?
  4. What makes this post different to other blog posts on this topic (on the GitLab blog or elsewhere)?
  5. What makes you the best person to write about this topic? What unique experience or insight do you bring to it?

Showing how your proposed blog posts meets the criteria and that you've given thought to the considerations above will give your pitch the best chance of being accepted.

Examples of strong blog post pitches

Below are links to blog post proposals that demonstrate the value the post will offer to the reader:

If your pitch is accepted

  1. Feel free to start your draft using this blog post template which has all the relevant information already embedded. Then see the formatting guidelines for help with creating and formatting your blog post MR.
  2. Use the Blog post merge request template for your MR and ensure it is set to close the associated issue automatically.
  3. Assign the issue and associated merge request to yourself and apply the appropriate labels based on what stage of creation you are on. Once it's ready for editorial review, assign both to @rebecca. She will assign a member of the Editorial team to review the post.

Note for the Content team: If your blog post is already scheduled and appears on the blog calendar please submit your MR and associated issue directly to @vsilverthorne or @skassabian for review.

Blog post ideas and proposals for Content Hack Day should be created in the Content Hack Day project. We keep these issues in a separate project because not all ideas and proposals that come from Hack Day will be worked on.

Time-sensitive posts: Official announcements, company updates, breaking changes, and news

Before you open an issue for making an official announcement or sharing a company update or other news on the blog, please see the PR handbook regarding announcements. In some cases we may communicate the announcement via channels other than the blog.

If the Corporate Communications team decides that we will go ahead with a blog post, please follow the process outlined below.

Posting to solicit feedback

If you want to solicit feedback on an issue (e.g. gathering community input on a proposal) or encourage people to take part in a survey, it is best to promote the issue or survey itself rather than publishing a blog post which then points people to another place to give their feedback. This has a few advantages:

  1. Your issue/survey remains the single source of truth for communication on the subject, rather than spreading communication across a blog post and another page.
  2. There is one fewer step/link to click on for community members to share their feedback.
  3. Keeping things in an issue/survey reinforces that a final decision has not yet been made, whereas a blog post can give the impression of being a formal announcement rather than a proposal. This can have a negative impact on brand sentiment.

We can spread the word about gathering feedback on something in the same way that we would promote a blog post:

  1. You can request promotion of your item on GitLab's social channels.
  2. You can request that your item be included in an upcoming newsletter by leaving a comment on the appropriate newsletter issue.
  3. Ask in #marketing on Slack if you would like to promote in some other way.

Joint announcements

Joint announcements with partners or other third parties should first be proposed to the Corporate Communications team ahead of writing to maximize impact and ensure alignment across teams. They will decide whether we issue a press release, blog post, or communicate via other channels. If they decide to move forward with a blog post, please follow the instructions to pitch the blog post idea to the Editorial team.

If we are doing a joint announcement with another company, the announcement post should appear on one party's site, to maximize impact. If both parties wish to publish a blog post about an announcement, the posts should be original and each take a unique angle, offering something new and valuable to the reader. In some cases this may mean a post takes more time to be researched, written, and reviewed, which may necessitate publishing shortly after the official announcement, if planning further ahead is not possible. Posts tend to get more traffic with this approach, rather than publishing and promoting duplicate posts on two blogs, or rushing to get a straightforward announcement out.

Process for time-sensitive posts

If you have followed the directions in the PR handbook and the Corporate Communications team has decided that a blog post is the best way to communicate your announcement, please follow the process below.

  1. Start by opening an issue in the project, using the Blog post template and applying the priority label. Even if you do not have a draft or a confirmed publish date, it's important to open the issue as far in advance as possible and ping @rebecca so she knows it is coming and can prioritize accordingly.
  2. The issue title should reflect the date on which you expect to publish.
  3. The issue due date should be a minimum of two working days before the publish date.
  4. If other due dates apply (for example, design assets are required) make sure the entire timeline and all the people responsible are captured in the issue description.
  5. In most cases, we expect that you or one of your team will write the post and create the merge request for it, with all images and other formatting included. Feel free to start your draft using this blog post template which has all the relevant information already embedded. Then see the formatting guidelines for help with creating and formatting your blog post MR.
  6. If you need assistance with drafting the post or creating the MR, please make this clear in your issue and we will confirm if this is possible within your timeframe.
  7. Use the Blog post merge request template for your MR and ensure it is set to close the associated issue automatically.
  8. Be sure to check the review app for your blog post or preview it locally to ensure images, headlines, etc. are formatted correctly before handing over.
  9. When your MR is ready for review, please assign it and the corresponding issue to @rebecca (or @vsilverthorne or @skassabian if Rebecca is OOO) a minimum of two working days ahead of when you expect to publish. Please submit your MR earlier if you can.
  10. When you've assigned your MR and issue to a reviewer, please change the due date on the issue to reflect the publishing date.
  11. Your reviewer may leave comments for you to address, in which case they will assign the MR back to you. When you have resolved all outstanding discussions, assign the MR back to the reviewer for final review and merging.

Who to assign your MR to – urgent posts

Please assign first to @rebecca. If she is OOO, assign to @vsilverthorne/@skassabian. If no one is available and you cannot wait until they return, please assign to a member of the Technical Writing team. Where possible, select the technical writer who is listed for the most relevant stage group. If you need immediate assistance/review, find a technical writer who is online on Slack to request this directly. Regardless, be sure to specify any time constraints around getting the content reviewed and posted.

Continuous delivery mindset: With time-sensitive posts, don't wait to publish a post if you have enough information to go live. It's OK to publish a headline and paragraph to get the news out in a timely matter and add more details later (e.g. add more graphics, charts, etc.). We don't want to miss out on a news cycle because we're waiting for an image or supplementary information.

Error budgets for time-sensitive posts

Inspired by error budgets used by Engineering.

Applied to the blog, error budgets incentivize forward planning and early communication of time-sensitive blog posts. This helps the Editorial team to minimize time spent on unplanned work, shuffling of the publishing schedule, or work that is subsequently wasted or does not serve our mission and vision, or the needs of our audience.

Each functional group is responsible for not exceeding an allowed budget of 15 points per quarter. The number of points given will depend on the severity of the impact to the team's workflow:

Where error budget points are incurred, a member of the Editorial team will apply the relevant label to the issue or merge request in question. Points will be totaled at the end of the quarter and communicated to functional groups who exceed their budgets, so that we can work together on solutions to prevent this in the future.

How to avoid incurring error budget points

Third-party posts

We will promote anyone integrating with GitLab, even if we compete with them. It is very important to demonstrate to our customers that we do not lock them in.

If you are a partner or industry-adjacent third party who wants to write for our blog, please contact the Partner Marketing team before proceeding. GitLab team members asking on behalf of a third party should do the same.

Blog posts concerning third parties or partners, whether they are to be published on the GitLab blog or externally, should follow the standard pitching process for blog posts.

As a rule, any blog post, regardless of the author, should offer some informational, educational, or entertainment value to the reader. We do not publish material that is exclusively promotional in nature.

Guest author bio

Please be sure to include a short bio of the guest author at the end of the post, using the format below:

### About the guest author

_Short bio describing who the author is and giving any relevant background information._

You can see a live example of a guest author bio here.

Disclosure process for posts concerning third parties

When proposing or requesting a blog post on a topic that concerns one of GitLab's business partners, it may be appropriate to seek feedback from the partner before publishing. If you are planning a blog post that discusses a partner in depth and you're not sure if it's appropriate to seek partner feedback, please ping a member of the Partner Marketing team on your issue for their input. If they think the feedback process is necessary, please follow the steps below.

1. Connect with Alliances

When brainstorming what topic to discuss and write about, loop in a partner manager from Alliances for feedback and buy-in during the 'ideation phase'. To check who the relevant partner manager is, share your blog post issue and ask in the #Alliances Slack channel or check out our partner dashboard.

At this stage you and Alliances can decide whether or not the blog post topic justifies seeking feedback from the partner in question. If so, please submit your draft blog post MR to the Editorial team for review a minimum of 5 working days before you plan to publish to ensure enough time for all parties to review.

2. Submit draft to partner for feedback

When the merge request has been reviewed by the Editorial team, please assign the blog post MR and associated issue to the partner manager to submit the draft to our partners for review while CC'ing the appropriate stakeholders from marketing for visibility. This should be done with 48 hours' courtesy notice, for the sole purpose of providing feedback on the following areas:

We should not proceed to publish the blog post until the partner manager has received the feedback and/or has received acknowledgment from the partner that they have read the draft within the 48-hour window. If we need to escalate within the Alliance team, @bjung is the highest escalation point within that team.

It should be noted that this is a courtesy review of the content, not a formal approval from our partner to publish the post. It should ultimately be GitLab's decision if to go live with the post or not.

Guest posts by GitLab partners

Official GitLab partners may use our CTA button and include promotional copy, however the blog post must still serve a function (informational or educational) other than simply promoting something.

For either type of guest post, the process and guidelines for publishing are as follows:

  1. Create an issue in the www-gitlab-com project and label it blog-post and guest/partner post.
  2. If technical input is required, we will ask for instructions from the third party; otherwise it is at the discretion of the blog editorial and technical writing teams whether or not to go forward.

Guest posts by non-partners

If the author of a guest post is not an official GitLab partner, they may link back to their website or own content with inline links, but may not include a CTA button or promotional copy.

Cross posts

We do not republish posts from other publications verbatim.

If you spot a tweet, post, or feedback anywhere detailing an interesting use case for GitLab which you think could make a good story, open an issue and ping @kimlock to help determine whether we should pursue an interview for a blog post or case study (we will not ask the original author to create something for us).

Pick Your Brain posts

For follow-up posts to Pick Your Brain interviews with the CEO, as soon as an interview or livestream is scheduled, please open an issue in the project, using the PYB blog post template, and fill out the relevant information there.

The blog editorial team will assess the content of the post and decide how best to leverage it. This may mean publishing on the blog, on our Medium site, simply sharing the video on social, or something else.

Once agreed, the blog team will assign and communicate the expected publish date.


Publishing schedule

With the exception of time-sensitive announcements and news, we are aiming to have blog posts scheduled two weeks ahead.

Blog calendar

The calendar below documents when posts will be published, as well as industry awareness days and anniversaries we may cover. Please bear this in mind when requesting a specific publish date for a post.

Please note that all dates are subject to change to accommodate urgent posts and announcements.

Writing blog posts – best practices

Based on an analysis of the traffic to blog posts from Q4 2019, it's clear that technical/actionable/how-to content attracts the most blog readers and thus should be the focus of the majority of the content we create for the blog.

Think of our audience first

When writing a blog post, please keep the reader in mind. This person has more than likely searched for a topic or a trend and is looking for comprehensive content with a clear takeaway. Write to this person and consider the tone and the language that will best get your point across. Our "How to get your blog post pitch accepted" section has some tips and prompts to help you prioritize our audience when planning your blog post.

Best practices for specific types of posts

GitLab or industry events

We conducted an audit of all event-supporting content for 2018-19 and shared the results. Below is a summary of the takeaways:


What matters to our readers is what matters to GitLab. A better way to cover events that offers something new with a specific angle is to take one trend or session and do a deep dive on it, rather than trying to cover the conference as a whole.

If you want to share photos from the event such as the GitLab booth or social events, consider sharing these on social channels or writing a post for the Unfiltered blog.

*From 2018 blog analysis conducted in 2018-10

  1. Average sessions per month on the blog: ~30,000
  2. Average sessions per post in published month: 3,792
  3. Average sessions per content marketing post in published month: 3,500
  4. 55% of posts get <1,000 sessions in a month
  5. 27.47% of posts hit our expected outcome of 1,000-4,999 sessions in published month
  6. 28.57% of posts garner less than 499 sessions in published month
  7. 9% of posts are "hits" (10K+ sessions in published month); "Hits" don't consistently perform well over time

Breakdown by category of "hits":


  1. There is not a strong correlation between # of sessions and topic
  2. Strong performing content marketing posts focus on show and tell engineering stories
  3. Posts really fall into the 5,000-9,999 session bracket (3.3%)
  4. Content hack day posts tend to perform well (>1,000 sessions in published month)

Identifying and qualifying a high-performing blog post

Qualifying story ideas:

Look for the following patterns:

  1. Team implementing a new technology, process, or coding language
  2. Deep dive into how a popular feature is made
  3. Chronicling a performance improvement
  4. Covering a controversial decision that was made

Who and how to interview:

  1. Contact the technical subject matter expert. This can be someone who created the issue, or managed the project.
  2. Set up a 30 minute interview and dig into:
    • What was the challenge?
    • What was the solution?
    • How did you go from point a to point b? Walk me through your thought process.
    • How was the solution implemented and what is a realistic use case of the solution?
    • What lessons were learned?
    • How did this make the GitLab product / development community better?
  3. Optional: Contact the business subject matter expert. This could be a product manager or a product marketing manager.
  4. Set up a 30 minute interview and dig into:
    • How does this solution help a user?
    • What business value does this solution bring?
    • How does this solution relate to our product?

Attributes of a successful blog post:

  1. Deep dive into a hard technical challenge.
  2. A behind-the-scenes look at how we tackled something: "How we built x," "How we solved y," "How we scaled z."
  3. Puts the reader in the shoes of the person who faced the challenge; reader learns via compelling example.
  4. Intellectually satisfying; learning component.
  5. Allows the reader to learn from someone else's mistake, and follows a problem/trials and triumphs/solution story arc.
  6. Taking a controversial or unpopular stance on a topic, backed by hard evidence.

Examples of high-performing posts (20K+ sessions in published month):

  1. Meet the GitLab Web IDE
  2. How a fix in Go 1.9 sped up our Gitaly service by 30x
  3. Hey, data teams - We're working on a tool just for you
  4. Why we chose Vue.js
  5. How we do Vue: one year later

Team members can also browse the Content Marketing Dashboard to see more examples of current blog posts drawing high traffic.

Conducting a blog analysis

Pull information on all blog posts to document how many sessions each post received in the month, and how many sessions they received of all time. Categorize them by type, bracket, total sessions in month, total sessions to date, category, theme, and topic. Eventually add first touch point revenue data. Search Google Drive for Blog Performance to find the appropriate sheet to work from.

Column explanations:

Preparing confidential blog posts

In the event that we have a big announcement to make and the information must remain confidential until, use the forked project to prepare your merge request.

Publishing natively on LinkedIn and Medium

Occasionally, some blog posts are better suited to publishing natively by the author on LinkedIn, or on Medium. We reach slightly different audiences on these channels, and some content will get better exposure and reach if published there. If you submit a blog post and the content team decides it's a better fit for one of these, we will let you know. For now, we will treat this segmentation as an experiment, monitor the performance of different types of content on different channels, and apply our learnings. We may then be able to start planning content for specific outlets and commission stories accordingly.

Formatting guidelines

Please see the publishing process before you get started.

To publish to the blog, you will need to create a merge request for the www-gitlab-com project with a file of your blog post content formatted in Markdown. Please read through the Markdown guide for reference.

If you like, you can start by drafting your post in a Google Doc (feel free to make a copy of this blog post template). Below are instructions for formatting the content of your file correctly. If you already have your content ready to go, you can jump ahead to see how to add your blog post file with a merge request.


The post frontmatter is the first part of any post. It is standard and cannot be changed, so please make sure to provide the correct information, and do not add nor remove anything from the default template. If you think one of the fields is unnecessary for your post, leave it blank and the editor who reviews your post may remove it.

title: "This is the post title"
author: Firstname Lastname # if name includes special characters use double quotes "First Last"
author_gitlab: username # ex: johndoe
author_twitter: Twitter username or gitlab # ex: johndoe
categories: company
image_title: '/images/blogimages/post-cover-image.jpg'
description: "Short description for the blog post"
tags: tag1, tag2, tag3
cta_button_text: 'Watch the XXX release webcast live!' # optional
cta_button_link: '' # optional
guest: true # required when the author is not a GitLab Team Member or there is more than one author
ee_cta: false # required only if you do not want to display the EE-trial banner
install_cta: false # required only if you do not want to display the 'Install GitLab' banner
twitter_text: "Text to tweet" # optional;  If no text is provided it will use post's title.
featured: yes # reviewer should set
postType: content definition # i.e.: content marketing, product, corporate
merch_banner: merch_one
merch_sidebar: merch_one

More information about each field can be found below.


Please see the style guide for advice on choosing a headline that will grab readers' attention and make your post easy to search for.


Write out the author's name here. If you want to credit multiple authors please use the following format:

Anthony Davanzo and Kelly Hair (see a live example of multiple authors)

Please include the guest: true field in the frontmatter as well.

Cover image

Please see below for instructions on sizing, formatting, and storing images.

Add featured: yes to the frontmatter of a post to create a featured post. The two most recent featured posts will be shown at the top of the blog in the featured section (even if more are tagged). To remove a post from being featured, remove the featured: yes line from the frontmatter.


Use only one of the following categories per post. Do not change the capitalization, spelling, or anything else, otherwise you'll create another category, which is something we don't want to do accidentally.

If you're not sure which category your post belongs in, just put a placeholder in your MR and leave a comment for your reviewer noting that.

We're working on improving category pages and tagging, but for now you can find posts under the same category by navigating to Dashes will be automatically added to multi-word categories and all of them will be lowercased in the URL. For example, the category "company" will be available under

Note that there's a CI test that checks for wrong or missing categories. If one of the above should change or a new category is added, don't forget to edit the following files:

  1. Rakefile (search for CATEGORIES) - responsible for the CI test
  2. source/includes/blog/category-nav.html.haml - responsible for the categories navbar
  3. source/handbook/marketing/blog/ - the handbook which you're currently reading
  4. If a category changes, you need to replace it in all posts. Here's a one-liner command you can run:

     find .sites/blog/source/blog/blog-posts -type f -exec sed -i 's/categories: old/categories: new/' {} \;

    where old is the old category and new is the new one.

  5. In case of a category change, a redirect should be added in (internal). This is the last step after all the above are merged into master and deployed. Ask a production engineer to help you.


The description meta tag is important for SEO, and is what appears on the blog index page. It is also part of Facebook Sharing and Twitter Cards. We set it up in the post frontmatter, as a short summary of what the post is about. Description is limited to 150, otherwise the text will be cut off on the index page.

It is mandatory for all the new posts, and it has been included in the default post frontmatter generated by the command rake new_post.


These are included to help readers find similar posts if they are interested in a particular subject. Tags appear at the bottom of each blog post, and clicking on a tag takes you to /blog/tags where you can view all tagged posts and browse by tag.

You can include as many tags as you like, separated by commas. Please only include tags from the following list, and note that they are case sensitive. If you think a tag should exist that isn't on this list, please leave a comment for the reviewer on your MR saying which tag you'd like to use and why it should be included.

GitLab Commit tags

In addition to the tags above, we also have tags for GitLab Commit events. If you want your blog post to appear on the relevant post-event GitLab Commit landing page, add the relevant tag to the frontmatter in the tags field. New tags will be added for each year of GitLab Commit.

Call to action

The CTA entry is optional; if you don't need to add any CTA to the hero, just omit both entries, leaving the frontmatter without them. Do not include any UTM parameters in the link. Always wrap their values with quotes.

The final result is a red button over the cover image of the post.

In the below example, the following was included in the blog post's frontmatter in order to generate the CTA button.

cta_button_text: 'Watch the 8.16 release webcast live!'
cta_button_link: ''

Hero CTA preview

EE trial banner

To not display the EE trial banner on the blog post, set ee_cta to false in the frontmatter. It is set to true by default, so there's no need to add ee_cta: true to the frontmatter. Only set to false on the rare occasion you have a strong reason to not display the banner (usually in the case of posts that are for developers and our open source community).

Post type

We use these to make it faster to track the effectiveness of different types of blog posts. There are three post type categories we use to differentiate blog content:

  1. Content marketing: Examples, education, reporting, storytelling, thought leadership, and use cases.
  2. Corporate: Company news, announcements, and community updates (ex: issue bash, contributor profiles).
  3. Product: Release posts, critical updates, and partnership announcements.

Use the postType frontmatter option to set the content definition.

Media embeds

We limit media embeds to the following providers:

Newsletter sign-up form

We can now embed a CTA for readers to sign up for our newsletter directly from the blog post page. There are two different designs. Use the following:

<%= partial "includes/blog/content-newsletter-cta", locals: { variant: "a" } %>


Newsletter sign-up CTA variant A


<%= partial "includes/blog/content-newsletter-cta", locals: { variant: "b" } %>


Newsletter sign-up CTA variant B

Note: this only works for blog post files with the extension

Click to tweet

Consider adding pull quotes as click-to-tweet boxes, to encourage and make it easier for readers to share key points from your blog post. This only works for blog post files with the extension

To add a click-to-tweet box, copy the below partial and enter the text and author fields. If the author doesn't have a Twitter account you can use gitlab instead.

<%= partial "includes/blog/tweet", locals: { text: '', author: '' } %>


  <%= partial "includes/blog/tweet", locals: { text: 'The Operations experience then should really just be that I go to work in the morning and see an email summary of what has happened, without me having to do anything', author: 'MarkPundsack' } %>


![Click to tweet](/images/handbook/marketing/click-to-tweet.png){: .shadow}

Merch banner and sidebar

These should be included by default on all blog posts. Use your discretion; if it doesn't seem appropriate to include merchandizing on a post (for example, if we're announcing a partnership or integration, if the blog post contains lots of images and a sidebar could distract from the content, or if the blog post is very technical in nature (a tutorial or post describing a security advisory, offensive security practices, etc) and merchandizing undermines the authenticity or intent of the content or author, you can leave out either the sidebar or both the sidebar and banner.

There are two steps to include these: Designate what merch sidebar and banner to add in the front matter example frontmatter above, and add the merch sidebar inside your content:

Merch sidebar

To add a merch sidebar to your blog post, you need to designate it in the front matter and include the follwing inside the content where you want the sidebar to appear.

<%= partial "includes/blog/blog-merch-sidebar-dynamic" %>

Merch banner

Merch banners are automatically added to the end of blog posts if they are designated in the frontmatter.

You can see a live example of the merchandizing in this blog post: Introducing Resource Groups.

You can also view the raw file of our example post to see how all the necessary elements have been added to the frontmatter and body of the post.

Current merch items

Merch items are defined in one data file and implemented accross all blog posts during the build process. You can see all current merch items in blog_merch.yml

  destination_url: "/resources/guide-to-the-cloud/"
  cta_text: "Learn more"
  banner_image_source: "/images/merchandising-content/mc-guide-to-app-security-ebook-vertical.png"
  banner_body_title: "Guide to the Cloud"
  banner_body_content: "Harness the power of the cloud with microservices, cloud-agnostic DevOps, and workflow portability."
  sidebar_image_source: "/images/merchandising-content/mc-guide-to-app-security-ebook-horizontal.png"
  sidebar_body_title: "Guide to the Cloud"
  sidebar_body_content: "Harness the power of the cloud with microservices, cloud-agnostic DevOps, and workflow portability."

  destination_url: "/compare/github-actions-alternative/"
  cta_text: "View now"
  banner_image_source: "/images/merchandising-content/mc-mastering-cicd-vertical.png"
  banner_body_title: "Master your CI/CD"
  banner_body_content: "Watch this webcast and learn to deliver faster with CI/CD."
  sidebar_image_source: "/images/merchandising-content/mc-mastering-cicd-horizontal.png"
  sidebar_body_title: "Master your CI/CD"
  sidebar_body_content: "Watch the webcast"

  destination_url: "/resources/ebook-single-app-cicd"
  cta_text: "Learn more"
  banner_image_source: "/images/merchandising-content/benefits-of-single-app-cicd.jpg"
  banner_body_title: "Free eBook: The benefits of single application CI/CD"
  banner_body_content: "Download the ebook to learn how you can utilize CI/CD without the costly integrations or plug-in maintenance."
  sidebar_image_source: "/images/merchandising-content/benefits-of-single-app-cicd.jpg"
  sidebar_body_title: "Single application CI/CD"
  sidebar_body_content: "How to reduce costly integrations and plug-in maintenance."

  destination_url: "/resources/ebook-ciso-secure-software/"
  cta_text: "Get the eBook"
  banner_image_source: "/images/merchandising-content/mc-10-steps-every-ciso-ebook-vertical.png"
  banner_body_title: "10 Steps Every CISO Should Take to Secure Next-Gen Software"
  banner_body_content: "Understand three software shifts impacting security, and the steps CISOs can take to protect their business."
  sidebar_image_source: "/images/merchandising-content/mc-10-steps-every-ciso-ebook-horizontal.png"
  sidebar_body_title: "10 Steps to secure next-gen software"
  sidebar_body_content: "Learn how DevOps will impact your security program."

- name: merch_five
  destination_url: "/webcast/cloud-native-transformation/"
  cta_text: "Learn more"
  banner_image_source: "/images/merchandising-content/cloud-native-transformation.png"
  banner_body_title: "A Cloud Native Transformation"
  banner_body_content: "Learn how Ask Media Group modernized their architecture and development with microservices, containers, and Kubernetes."


Comments are present in all posts by default. Set it to false only if you have a strong reason to do so (comments: false). They are our best source of feedback on posts.

Adding code blocks

Below are the two types of code blocks we commonly use on the blog. Find a number of other options in the Markdown guide.

In-line code

We use this for short words or phrases included in a paragraph. For inline code, surround the word or code with single backticks (`).


This is an `in-line` code block.

Results in:

This is an in-line code block.

Fenced code blocks

"Fenced" code blocks look like the block below. We use these for longer code snippets. To create a fenced code block, put triple backticks on one line directly above and one line directly below the code.


how to create fenced code block

Results in:

this is my code block
   here's another line

Highlighted code

Syntax highlighting helps make code easier to read. In order to enable syntax highlighting please append the language type at the end of the code block. The name matters because every language is highlighted differently.

Example (not highlighted):

```code goes here```
document.querySelectorAll('a[href^="#"]').forEach(elem => {
    elem.addEventListener('click', e => {
        let block = document.querySelector(elem.getAttribute('href')),
            offset = elem.dataset.offset ? parseInt(elem.dataset.offset) : 0,
            bodyOffset = document.body.getBoundingClientRect().top;
            top: block.getBoundingClientRect().top - bodyOffset + offset,
            behavior: "smooth"

Versus (highlighted javascript):

```code goes here```javascript

(or other languages/syntaxes such as yaml, ruby, sql, etc)

document.querySelectorAll('a[href^="#"]').forEach(elem => {
    elem.addEventListener('click', e => {
        let block = document.querySelector(elem.getAttribute('href')),
            offset = elem.dataset.offset ? parseInt(elem.dataset.offset) : 0,
            bodyOffset = document.body.getBoundingClientRect().top;
            top: block.getBoundingClientRect().top - bodyOffset + offset,
            behavior: "smooth"

Images and illustration

Blog images are stored in the source/images/blogimages/ directory. If your post contains many images, create a sub-directory for your post: source/images/blogimages/name-of-post/.

Preparing images

Image attribution

The only images accepted for are public domain images and screenshots. Whenever you choose an image which is not a screenshot, add a link to the original image to the merge request description and as an HTML comment:

<!-- image: image-url -->

Do the same for cover images, adding a link to the original image to the end of the post:

Cover image by [owner name and surname](link) on [Unsplash](link)
{: .note}

You can paste this from the box that appears on Unsplash when you have downloaded an image. For images not from Unsplash:

[Cover image](link-to-original-image) by [owner name and surname](link), licensed under [CC X](link-to-licence)
{: .note}

Inline images

To add an image inline, insert the following into your markdown file, where you want the image to appear:

![Alt text for your image](/images/blogimages/your-image-filename.jpg){: .shadow}

The alt text for your images should describe the content and function of your image for visitors using a screen reader.

Sizing and aligning images

There are new classes for sizing and positioning blog post images. They should be added to blog post images similar to how the shadow class is added already:

![Your image alt text](/images/blogimages/your-image-filename.jpg){: .shadow.small.left.wrap-text}

The new classes are grouped into:




.wrap-text: Only applies to images that are positioned left or right. Makes text wrap around the image.

These classes are only applied for screens 992px wide and wider.

If a Position class is used without an Effect class then there will simply be empty space where the image isn't. Similarly, using a Position class without a Size class will have little-to-no effect.

Image captions

Insert the following beneath an inline image to include a caption:

This is my image caption
{: .note.text-center}

This will look as below:

This is my image caption


For technical/tutorial posts, please illustrate your examples with code blocks or screenshots. Be consistent with your examples. E.g., if you are using a generic URL to exemplify your steps, be consistent and keep it, throughout the post.

Important security point: Do not expose your personal details by using your real tokens or security credentials. Use placeholders such as [project's CI token] stub instead. Or blur them if displayed on screenshots.

Embedding videos

Please see the Markdown Guide for instructions for embedding videos from YouTube and other sources.

If appropriate, please add a video credit, for example:

Video directed and produced by [Aricka Flowers](/company/team/#arickaflowers)
{: .note}

Embedding tweets or Instagram posts

Please see the Markdown guide for instructions for embedding posts from social media.

Creating GIFs

Animated GIFs are very useful to illustrate short dynamic processes, which might be easier to understand with this kind of resource. There are a few ways to create animated GIFs, one of them is using [Giphy Capture], a light-weight app for Mac.

Avoid GIFs with a huge file size, they will be difficult to load for users with bad internet connection. In those cases, you can either cut the GIFs in smaller pieces, or record a video, or use a sequential image.

Read more on Making Gifs in the Product Handbook.


If you create your own cover image, it should have the following proportions:

Try to have them harmonically aligned with the title, which overlays the background image in both cases. To crop the image, use the size of 1275x750 px. If you want to align the background image with the title overlay, use the widescreen proportion.

When your post is formatted and you're ready to create your merge request

You can go about this a couple of ways: by adding a new file to sites/blog/source/blog/blog-posts/ in the UI, or using the terminal on your own computer.

Creating a blog post MR from the UI

You should have an issue for the blog post you are writing. Go to the issue and click Create merge request.

Create merge request button

Now you have a merge request! Please edit the title to be "WIP: blog post - Title of blog post".

Placing "blog post" in your MR title will trigger a blog-only build for review apps, which means you will be able to preview your blog post much sooner.

Make sure you select the blog post template for your MR, as this contains some important tasks for you to complete.

MR template for blog post

You can assign the MR to yourself while you are working on it.

Click on the link to the right of Request to merge (this is the name of your branch).

branch name

Now navigate to sites/blog/source/blog/blog-posts/ in the tree view, then click on the + dropdown menu and select New file.

New file

Enter your filename using the format This will become the URL for the blog post so please keep that in mind and don't use something like my-blog-post-draft! 😄

New file

Please do not include special characters, capital letters, or numbers (other than the date) in your filename as this can lead to issues with publishing and can break the link to your review app.

If you aren't sure when the post will be published, just choose an arbitrary date in the future – we will update it before publishing.

Now you can paste your post content into the window. If pasting from a Google Doc or another word processor, please make sure you convert it to plain text first. A quick way to do this is to paste everything into the URL bar of a browser, and paste it into your blog post file from there.

Example blog post file Note that there is no blank line at the top or lines in between your front matter items.

When ready, scroll down and enter a description of what you've just done in the Commit message box. Make sure the Target branch is correct (i.e. has the number and title of the issue for your blog post) then click Commit changes.

commit message

You will then see your added file. To go back to your MR, it's easiest to go to your MRs in your dashboard (this is why it's important to assign the MR to yourself when you create it). You'll find all MRs assigned to you in the top right corner of your screen.

MR dashboard

In your MR, if you scroll down and go to the Changes tab, you should see the blog post file you added there.

Changes tab

To see a preview of your post as it will look when it's live, you will need to wait for the pipeline to pass. You can check its progress from the Pipelines tab. When it passes, you can click on View app near the top of your MR and it will open a new tab with your preview. Please check your review app before asking anyone else to review.

Link to review app

If you need to make changes to your file, you can edit it by clicking on the edit button, which will take you to the same interface as when you added your new file. Just make your changes, edit the commit message to say what you did and hit Commit changes.

Edit button

When you're ready, assign the MR to a fellow team member to review your post. When they have reviewed and you've addressed any comments they had and resolved any outstanding discussions, please assign your MR and the corresponding issue to @rebecca to review.

You may find this video walkthrough helpful for creating your blog post MR from the UI:

Creating a post from the command line

You will need to be set up to edit the website locally in order to create your post this way. Please see GitLab 102 for instructions. If you're already set up:

rake new_post

Hit enter or return, then you'll be prompted to enter the post title. Type in the title exactly as you want it, for example "Hello World – I'm a new post" and rake will take care of the filename for you. Then you just open the file, fill out the front matter and start writing, or paste your (plain text) draft in from your Google Doc.

Adding a cover image to your blog post MR

Most blog posts require a new cover image. You should upload your chosen image to the same branch as your blog post file, so that it is all included in the same MR. This means it will be published to the website simultaneously, and also allows you to preview your cover image in the review app.

Step 1: Upload image file

When you have selected an image to use, save it on your computer, ensuring you don't include any capital letters, spaces, or special characters in the filename.

Now go to your MR and click on the branch name afterRequest to merge.

branch name

Navigate to source/images/blogimages/ in the tree view, then click on the + dropdown menu and select Upload file

Upload file

Drag and drop or click to upload your image file, then enter a description of what you've just done in the Commit message box. Make sure the Target branch is correct (i.e. has the number and title of the issue for your blog post) then click Upload file.

To go back to your MR, it's easiest to go to your MRs in your dashboard (this is why it's important to assign the MR to yourself when you create it). You'll find all MRs assigned to you in the top right corner of your screen.

MR dashboard

In your MR, if you scroll down and go to the Changes tab, you should see the blog post file you added there.

Changes tab

Step 2: Add file path to blog post file

Make a note of the name of your image.

Image file name

Scroll down to your blog post file and click on the pencil icon (Edit file button).

Edit button

This will take you to the same interface as when you added your new file. In the frontmatter, look for this field:

image_title: '/images/blogimages/post-cover-image.jpg'

Change the image filename to the name of the image you just uploaded. Ensure that you keep the whole path the same (so, starting with /images/blogimages/). Now edit the commit message to say what you did and hit Commit changes.

Go back to your MR. If everything has been done correctly, when the pipeline passes your review app should show the cover image as expected.

Link to review app

What if my cover image isn't showing?

This is usually because the image filename and what you entered in the frontmatter do not correspond. Look out for typos in either the filename or in the frontmatter.

Make sure that the image path is correct (i.e. it begins with /images/blogimages/ – no missing slash at the front, no source before the first slash).

It often helps to look at the file of another recent blog post that is already live and compare it to your file to see if there are any discrepancies.