[{"data":1,"prerenderedAt":1659},["ShallowReactive",2],{"/blog/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1":3,"navigation-en-us":872,"banner-en-us":1300,"footer-en-us":1309,"blog-post-authors-en-us-Alexander Malaev":1554,"blog-related-posts-en-us-building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1":1569,"blog-promotions-en-us":1595,"next-steps-en-us":1649},{"id":4,"title":5,"authors":6,"body":8,"category":832,"date":833,"description":834,"extension":835,"externalUrl":836,"faq":836,"featured":837,"heroImage":838,"meta":839,"navigation":862,"path":863,"seo":864,"slug":868,"stem":869,"tags":836,"template":870,"updatedDate":836,"__hash__":871},"blogPosts/en-us/blog/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1.md","Building an Elixir Release into a Docker image using GitLab CI - Part 1",[7],"Alexander Malaev",{"type":9,"value":10,"toc":827},"minimark",[11,19,22,51,56,62,69,144,147,219,230,233,336,339,435,438,441,444,493,499,601,607,610,672,675,678,683,715,718,723,778,781,785,802,806,809,820,823],[12,13,14,18],"p",{},[15,16,17],"strong",{},"Note:"," this post is a customer story by Alexander Malaev, a software developer.",[12,20,21],{},"Well, we are actively using Phoenix/Elixir in our projects for backend development, we also have a RoR project as a frontend-service for our Admin UI. Our project consists of a bunch of microservices written in Elixir/Erlang, and we are running it in production with Docker-containers linked together and composed by Docker-compose.",[12,23,24,25,29,30,33,34,38,39,42,43,46,47,50],{},"On every push to a project's branch on ",[26,27,28],"span",{},"GitLab",", ",[26,31,32],{},"GitLab CI"," runs tests, style checking, and other tasks. These tasks are configured using ",[35,36,37],"code",{},".gitlab-ci.yml",". On every merge to ",[35,40,41],{},"master"," GitLab builds a release image for us and uploads it to [GitLab Container Registry]",[26,44,45],{},"registry",". After all, we run ",[35,48,49],{},"docker-compose pull && docker-compose up -d"," on the servers to download the latest release images and upgrade our containers.",[52,53,55],"h2",{"id":54},"ci-pipeline","CI pipeline",[12,57,58,59,61],{},"So, in the following I will describe our release pipeline for Elixir services, using snippets from our project’s ",[35,60,37],{},".",[12,63,64,65,68],{},"We are using ",[35,66,67],{},"docker:latest"," image for our Runner, and several stages:",[70,71,76],"pre",{"className":72,"code":73,"language":74,"meta":75,"style":75},"language-yaml shiki shiki-themes github-light","image: docker:latest\nstages:\n  - build\n  - styles\n  - test\n  - release\n  - cleanup\n\n","yaml","",[35,77,78,94,103,112,120,128,136],{"__ignoreMap":75},[26,79,82,86,90],{"class":80,"line":81},"line",1,[26,83,85],{"class":84},"shJU0","image",[26,87,89],{"class":88},"sgsFI",": ",[26,91,93],{"class":92},"sYBdl","docker:latest\n",[26,95,97,100],{"class":80,"line":96},2,[26,98,99],{"class":84},"stages",[26,101,102],{"class":88},":\n",[26,104,106,109],{"class":80,"line":105},3,[26,107,108],{"class":88},"  - ",[26,110,111],{"class":92},"build\n",[26,113,115,117],{"class":80,"line":114},4,[26,116,108],{"class":88},[26,118,119],{"class":92},"styles\n",[26,121,123,125],{"class":80,"line":122},5,[26,124,108],{"class":88},[26,126,127],{"class":92},"test\n",[26,129,131,133],{"class":80,"line":130},6,[26,132,108],{"class":88},[26,134,135],{"class":92},"release\n",[26,137,139,141],{"class":80,"line":138},7,[26,140,108],{"class":88},[26,142,143],{"class":92},"cleanup\n",[12,145,146],{},"Passing some variables:",[70,148,150],{"className":72,"code":149,"language":74,"meta":75,"style":75},"variables:\n  APP_NAME: project\n  APP_VERSION: 0.0.1\n  CONTAINER_RELEASE_IMAGE: gitlab.example.org/example/project:latest\n  POSTGRES_HOST: postgres\n  POSTGRES_USER: postgres\n  POSTGRES_PASSWORD: password\n\n",[35,151,152,159,169,180,190,200,209],{"__ignoreMap":75},[26,153,154,157],{"class":80,"line":81},[26,155,156],{"class":84},"variables",[26,158,102],{"class":88},[26,160,161,164,166],{"class":80,"line":96},[26,162,163],{"class":84},"  APP_NAME",[26,165,89],{"class":88},[26,167,168],{"class":92},"project\n",[26,170,171,174,176],{"class":80,"line":105},[26,172,173],{"class":84},"  APP_VERSION",[26,175,89],{"class":88},[26,177,179],{"class":178},"sYu0t","0.0.1\n",[26,181,182,185,187],{"class":80,"line":114},[26,183,184],{"class":84},"  CONTAINER_RELEASE_IMAGE",[26,186,89],{"class":88},[26,188,189],{"class":92},"gitlab.example.org/example/project:latest\n",[26,191,192,195,197],{"class":80,"line":122},[26,193,194],{"class":84},"  POSTGRES_HOST",[26,196,89],{"class":88},[26,198,199],{"class":92},"postgres\n",[26,201,202,205,207],{"class":80,"line":130},[26,203,204],{"class":84},"  POSTGRES_USER",[26,206,89],{"class":88},[26,208,199],{"class":92},[26,210,211,214,216],{"class":80,"line":138},[26,212,213],{"class":84},"  POSTGRES_PASSWORD",[26,215,89],{"class":88},[26,217,218],{"class":92},"password\n",[12,220,221,222,225,226,229],{},"These variables are used during the release's build, so they will be available for all the stages. E.g., ",[35,223,224],{},"CONTAINER_RELEASE_IMAGE"," is used on the release stage, as a link to push the release image to. The ",[35,227,228],{},"POSTGRES_*"," variables are used to configure postgres service, and to connect later from containers.",[12,231,232],{},"Our build stage:",[70,234,236],{"className":72,"code":235,"language":74,"meta":75,"style":75},"build:\n  before_script:\n    - docker build -f Dockerfile.build -t ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF .\n    - docker create\n      -v /build/deps\n      -v /build/_build\n      -v /build/rel\n      -v /root/.cache/rebar3/\n      --name build_data_$CI_PROJECT_ID_$CI_BUILD_REF busybox /bin/true\n  tags:\n    - docker\n  stage: build\n  script:\n    - docker run --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF --rm -t ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF\n\n",[35,237,238,245,252,260,267,272,277,282,288,294,302,310,320,328],{"__ignoreMap":75},[26,239,240,243],{"class":80,"line":81},[26,241,242],{"class":84},"build",[26,244,102],{"class":88},[26,246,247,250],{"class":80,"line":96},[26,248,249],{"class":84},"  before_script",[26,251,102],{"class":88},[26,253,254,257],{"class":80,"line":105},[26,255,256],{"class":88},"    - ",[26,258,259],{"class":92},"docker build -f Dockerfile.build -t ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF .\n",[26,261,262,264],{"class":80,"line":114},[26,263,256],{"class":88},[26,265,266],{"class":92},"docker create\n",[26,268,269],{"class":80,"line":122},[26,270,271],{"class":92},"      -v /build/deps\n",[26,273,274],{"class":80,"line":130},[26,275,276],{"class":92},"      -v /build/_build\n",[26,278,279],{"class":80,"line":138},[26,280,281],{"class":92},"      -v /build/rel\n",[26,283,285],{"class":80,"line":284},8,[26,286,287],{"class":92},"      -v /root/.cache/rebar3/\n",[26,289,291],{"class":80,"line":290},9,[26,292,293],{"class":92},"      --name build_data_$CI_PROJECT_ID_$CI_BUILD_REF busybox /bin/true\n",[26,295,297,300],{"class":80,"line":296},10,[26,298,299],{"class":84},"  tags",[26,301,102],{"class":88},[26,303,305,307],{"class":80,"line":304},11,[26,306,256],{"class":88},[26,308,309],{"class":92},"docker\n",[26,311,313,316,318],{"class":80,"line":312},12,[26,314,315],{"class":84},"  stage",[26,317,89],{"class":88},[26,319,111],{"class":92},[26,321,323,326],{"class":80,"line":322},13,[26,324,325],{"class":84},"  script",[26,327,102],{"class":88},[26,329,331,333],{"class":80,"line":330},14,[26,332,256],{"class":88},[26,334,335],{"class":92},"docker run --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF --rm -t ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF\n",[12,337,338],{},"Before running this stage, we create a container which provides volumes for building artifacts. By the way, GitLab CI has a cache volume itself for similar purposes, but I couldn’t make it working correctly with GitLab Runner using Docker image.",[70,340,342],{"className":72,"code":341,"language":74,"meta":75,"style":75},"test:\n  services:\n    - postgres\n  tags:\n    - docker\n  stage: test\n  script:\n    - env\n    - docker run --rm\n      --link $POSTGRES_NAME:postgres\n      -e POSTGRES_HOST=$POSTGRES_HOST\n      -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD\n      -e POSTGRES_USER=$POSTGRES_USER\n      -e MIX_ENV=$MIX_ENV\n      --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF sh -c \"mix ecto.setup && mix test\"\n\n",[35,343,344,351,358,364,370,376,384,390,397,404,409,414,419,424,429],{"__ignoreMap":75},[26,345,346,349],{"class":80,"line":81},[26,347,348],{"class":84},"test",[26,350,102],{"class":88},[26,352,353,356],{"class":80,"line":96},[26,354,355],{"class":84},"  services",[26,357,102],{"class":88},[26,359,360,362],{"class":80,"line":105},[26,361,256],{"class":88},[26,363,199],{"class":92},[26,365,366,368],{"class":80,"line":114},[26,367,299],{"class":84},[26,369,102],{"class":88},[26,371,372,374],{"class":80,"line":122},[26,373,256],{"class":88},[26,375,309],{"class":92},[26,377,378,380,382],{"class":80,"line":130},[26,379,315],{"class":84},[26,381,89],{"class":88},[26,383,127],{"class":92},[26,385,386,388],{"class":80,"line":138},[26,387,325],{"class":84},[26,389,102],{"class":88},[26,391,392,394],{"class":80,"line":284},[26,393,256],{"class":88},[26,395,396],{"class":92},"env\n",[26,398,399,401],{"class":80,"line":290},[26,400,256],{"class":88},[26,402,403],{"class":92},"docker run --rm\n",[26,405,406],{"class":80,"line":296},[26,407,408],{"class":92},"      --link $POSTGRES_NAME:postgres\n",[26,410,411],{"class":80,"line":304},[26,412,413],{"class":92},"      -e POSTGRES_HOST=$POSTGRES_HOST\n",[26,415,416],{"class":80,"line":312},[26,417,418],{"class":92},"      -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD\n",[26,420,421],{"class":80,"line":322},[26,422,423],{"class":92},"      -e POSTGRES_USER=$POSTGRES_USER\n",[26,425,426],{"class":80,"line":330},[26,427,428],{"class":92},"      -e MIX_ENV=$MIX_ENV\n",[26,430,432],{"class":80,"line":431},15,[26,433,434],{"class":92},"      --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF sh -c \"mix ecto.setup && mix test\"\n",[12,436,437],{},"Notice that we must pass the variables and link postgres manually, since GitLab Runner is passing the variables only to the first level of Docker, but we go deeply ;)",[12,439,440],{},"We could link as many services as we want. For example, we are using Kafka for production, and on our test stage we make Kafka service available for running tests.",[12,442,443],{},"Style checking:",[70,445,447],{"className":72,"code":446,"language":74,"meta":75,"style":75},"styles:\n  tags:\n    - docker\n  stage: styles\n  script:\n    - docker run --rm\n      --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF sh -c \"mix credo --strict\"\n\n",[35,448,449,456,462,468,476,482,488],{"__ignoreMap":75},[26,450,451,454],{"class":80,"line":81},[26,452,453],{"class":84},"styles",[26,455,102],{"class":88},[26,457,458,460],{"class":80,"line":96},[26,459,299],{"class":84},[26,461,102],{"class":88},[26,463,464,466],{"class":80,"line":105},[26,465,256],{"class":88},[26,467,309],{"class":92},[26,469,470,472,474],{"class":80,"line":114},[26,471,315],{"class":84},[26,473,89],{"class":88},[26,475,119],{"class":92},[26,477,478,480],{"class":80,"line":122},[26,479,325],{"class":84},[26,481,102],{"class":88},[26,483,484,486],{"class":80,"line":130},[26,485,256],{"class":88},[26,487,403],{"class":92},[26,489,490],{"class":80,"line":138},[26,491,492],{"class":92},"      --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF sh -c \"mix credo --strict\"\n",[12,494,495,496,498],{},"Release task; we run it only on pushes to ",[35,497,41],{},":",[70,500,502],{"className":72,"code":501,"language":74,"meta":75,"style":75},"release:\n  tags:\n    - docker\n  stage: release\n  script:\n    - docker run\n      --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF\n      -e MIX_ENV=prod --rm -t ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF\n      sh -c \"mix deps.get && mix compile && mix release\"\n    - docker cp build_data_$CI_PROJECT_ID_$CI_BUILD_REF:/build/rel/$APP_NAME/releases/$APP_VERSION/$APP_NAME.tar.gz .\n    - docker build -t $CONTAINER_RELEASE_IMAGE .\n    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN gitlab.example.org:4567\n    - docker push $CONTAINER_RELEASE_IMAGE\n  only:\n    - master\n\n",[35,503,504,511,517,523,531,537,544,549,554,559,566,573,580,587,594],{"__ignoreMap":75},[26,505,506,509],{"class":80,"line":81},[26,507,508],{"class":84},"release",[26,510,102],{"class":88},[26,512,513,515],{"class":80,"line":96},[26,514,299],{"class":84},[26,516,102],{"class":88},[26,518,519,521],{"class":80,"line":105},[26,520,256],{"class":88},[26,522,309],{"class":92},[26,524,525,527,529],{"class":80,"line":114},[26,526,315],{"class":84},[26,528,89],{"class":88},[26,530,135],{"class":92},[26,532,533,535],{"class":80,"line":122},[26,534,325],{"class":84},[26,536,102],{"class":88},[26,538,539,541],{"class":80,"line":130},[26,540,256],{"class":88},[26,542,543],{"class":92},"docker run\n",[26,545,546],{"class":80,"line":138},[26,547,548],{"class":92},"      --volumes-from build_data_$CI_PROJECT_ID_$CI_BUILD_REF\n",[26,550,551],{"class":80,"line":284},[26,552,553],{"class":92},"      -e MIX_ENV=prod --rm -t ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF\n",[26,555,556],{"class":80,"line":290},[26,557,558],{"class":92},"      sh -c \"mix deps.get && mix compile && mix release\"\n",[26,560,561,563],{"class":80,"line":296},[26,562,256],{"class":88},[26,564,565],{"class":92},"docker cp build_data_$CI_PROJECT_ID_$CI_BUILD_REF:/build/rel/$APP_NAME/releases/$APP_VERSION/$APP_NAME.tar.gz .\n",[26,567,568,570],{"class":80,"line":304},[26,569,256],{"class":88},[26,571,572],{"class":92},"docker build -t $CONTAINER_RELEASE_IMAGE .\n",[26,574,575,577],{"class":80,"line":312},[26,576,256],{"class":88},[26,578,579],{"class":92},"docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN gitlab.example.org:4567\n",[26,581,582,584],{"class":80,"line":322},[26,583,256],{"class":88},[26,585,586],{"class":92},"docker push $CONTAINER_RELEASE_IMAGE\n",[26,588,589,592],{"class":80,"line":330},[26,590,591],{"class":84},"  only",[26,593,102],{"class":88},[26,595,596,598],{"class":80,"line":431},[26,597,256],{"class":88},[26,599,600],{"class":92},"master\n",[12,602,603,604,61],{},"We are using Conform to achieve runtime configuration of the release using environment variables. I use the approach described on this [blog post]",[26,605,606],{},"post-env",[12,608,609],{},"Task to cleanup things:",[70,611,613],{"className":72,"code":612,"language":74,"meta":75,"style":75},"cleanup_job:\n  tags:\n    - docker\n  stage: cleanup\n  script:\n    - docker rm -v build_data_$CI_PROJECT_ID_$CI_BUILD_REF\n    - docker rmi ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF\n  when: always\n\n",[35,614,615,622,628,634,642,648,655,662],{"__ignoreMap":75},[26,616,617,620],{"class":80,"line":81},[26,618,619],{"class":84},"cleanup_job",[26,621,102],{"class":88},[26,623,624,626],{"class":80,"line":96},[26,625,299],{"class":84},[26,627,102],{"class":88},[26,629,630,632],{"class":80,"line":105},[26,631,256],{"class":88},[26,633,309],{"class":92},[26,635,636,638,640],{"class":80,"line":114},[26,637,315],{"class":84},[26,639,89],{"class":88},[26,641,143],{"class":92},[26,643,644,646],{"class":80,"line":122},[26,645,325],{"class":84},[26,647,102],{"class":88},[26,649,650,652],{"class":80,"line":130},[26,651,256],{"class":88},[26,653,654],{"class":92},"docker rm -v build_data_$CI_PROJECT_ID_$CI_BUILD_REF\n",[26,656,657,659],{"class":80,"line":138},[26,658,256],{"class":88},[26,660,661],{"class":92},"docker rmi ci-project-build-$CI_PROJECT_ID:$CI_BUILD_REF\n",[26,663,664,667,669],{"class":80,"line":284},[26,665,666],{"class":84},"  when",[26,668,89],{"class":88},[26,670,671],{"class":92},"always\n",[12,673,674],{},"It removes the container with volumes created for build artifacts, and removes the image used during the pipeline. This task is running every time, despite the results of any previous tasks.",[12,676,677],{},"Below are our Dockerfiles:",[12,679,680,498],{},[35,681,682],{},"Dockerfile.build",[70,684,688],{"className":685,"code":686,"language":687,"meta":75,"style":75},"language-dockerfile shiki shiki-themes github-light","FROM msaraiva/elixir-gcc\nRUN apk add postgresql-client erlang-xmerl erlang-tools --no-cache\nWORKDIR /build\nADD . /build\nCMD mix deps.get\n","dockerfile",[35,689,690,695,700,705,710],{"__ignoreMap":75},[26,691,692],{"class":80,"line":81},[26,693,694],{},"FROM msaraiva/elixir-gcc\n",[26,696,697],{"class":80,"line":96},[26,698,699],{},"RUN apk add postgresql-client erlang-xmerl erlang-tools --no-cache\n",[26,701,702],{"class":80,"line":105},[26,703,704],{},"WORKDIR /build\n",[26,706,707],{"class":80,"line":114},[26,708,709],{},"ADD . /build\n",[26,711,712],{"class":80,"line":122},[26,713,714],{},"CMD mix deps.get\n",[12,716,717],{},"This image is used to create a container for running tests and style checks.",[12,719,720,498],{},[35,721,722],{},"Dockerfile",[70,724,726],{"className":685,"code":725,"language":687,"meta":75,"style":75},"FROM alpine:edge\nRUN apk — update add postgresql-client erlang erlang-sasl erlang-crypto erlang-syntax-tools && rm -rf /var/cache/apk/*\nENV APP_NAME project\nENV PORT 4000\nRUN mkdir -p /app\nCOPY $APP_NAME.tar.gz /app/\nWORKDIR /app\nRUN tar -zxvf $APP_NAME.tar.gz\nEXPOSE $PORT\nCMD trap exit TERM; /app/bin/$APP_NAME foreground & wait\n",[35,727,728,733,738,743,748,753,758,763,768,773],{"__ignoreMap":75},[26,729,730],{"class":80,"line":81},[26,731,732],{},"FROM alpine:edge\n",[26,734,735],{"class":80,"line":96},[26,736,737],{},"RUN apk — update add postgresql-client erlang erlang-sasl erlang-crypto erlang-syntax-tools && rm -rf /var/cache/apk/*\n",[26,739,740],{"class":80,"line":105},[26,741,742],{},"ENV APP_NAME project\n",[26,744,745],{"class":80,"line":114},[26,746,747],{},"ENV PORT 4000\n",[26,749,750],{"class":80,"line":122},[26,751,752],{},"RUN mkdir -p /app\n",[26,754,755],{"class":80,"line":130},[26,756,757],{},"COPY $APP_NAME.tar.gz /app/\n",[26,759,760],{"class":80,"line":138},[26,761,762],{},"WORKDIR /app\n",[26,764,765],{"class":80,"line":284},[26,766,767],{},"RUN tar -zxvf $APP_NAME.tar.gz\n",[26,769,770],{"class":80,"line":290},[26,771,772],{},"EXPOSE $PORT\n",[26,774,775],{"class":80,"line":296},[26,776,777],{},"CMD trap exit TERM; /app/bin/$APP_NAME foreground & wait\n",[12,779,780],{},"This Dockerfile is used to build an actual image with the Elixir release.",[52,782,784],{"id":783},"existing-problems","Existing problems",[786,787,788,792,795],"ul",{},[789,790,791],"li",{},"Now we don’t use the \"Erlang hot upgrade\" feature;",[789,793,794],{},"We don’t test if the release is correctly starting, now we are testing it manually and locally;",[789,796,797,798,801],{},"Every container uses its own \"epmd\" and intercommunication between the services, now made using REST apis, but I’m working on integration of [Erlang-In-Docker approach]",[26,799,800],{},"approach"," to use native erlang messaging between services.",[52,803,805],{"id":804},"whats-next","What’s next?",[12,807,808],{},"I have a plan to write and publish several articles about our release pipeline, to answer the following questions:",[786,810,811,814,817],{},[789,812,813],{},"How do we compile and publish assets?",[789,815,816],{},"How do we run our database migrations, since mix tasks aren’t available from the release image?",[789,818,819],{},"What problems are we facing during the implementation of this pipeline, and what solutions have we found.",[12,821,822],{},"Thanks for reading!",[824,825,826],"style",{},"html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}",{"title":75,"searchDepth":96,"depth":96,"links":828},[829,830,831],{"id":54,"depth":96,"text":55},{"id":783,"depth":96,"text":784},{"id":804,"depth":96,"text":805},"engineering","2016-08-11","Deploying projects written in Elixir/Erlang to production with Docker Containers and GitLab CI!","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749665223/Blog/Hero%20Images/containers.jpg",{"excerpt":840},{"type":9,"value":841},[842,846,848],[12,843,844,18],{},[15,845,17],{},[12,847,21],{},[12,849,24,850,29,852,33,854,38,856,42,858,46,860,50],{},[26,851,28],{},[26,853,32],{},[35,855,37],{},[35,857,41],{},[26,859,45],{},[35,861,49],{},true,"/en-us/blog/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1",{"title":5,"description":834,"ogTitle":5,"ogDescription":834,"noIndex":837,"ogImage":838,"ogUrl":865,"ogSiteName":866,"ogType":867,"canonicalUrls":865},"https://about.gitlab.com/blog/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1","https://about.gitlab.com","article","building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1","en-us/blog/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1","BlogPost","qHYyVuM-mgXnTCmnT-PC3pNNjw-nJDCAZDKtnhvFvus",{"logo":873,"freeTrial":878,"sales":883,"login":888,"items":893,"search":1220,"minimal":1251,"duo":1270,"switchNav":1279,"pricingDeployment":1290},{"config":874},{"href":875,"dataGaName":876,"dataGaLocation":877},"/","gitlab logo","header",{"text":879,"config":880},"Get free trial",{"href":881,"dataGaName":882,"dataGaLocation":877},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":884,"config":885},"Request a demo",{"href":886,"dataGaName":887,"dataGaLocation":877},"/sales/?contact-topic=request-demo","sales",{"text":889,"config":890},"Sign in",{"href":891,"dataGaName":892,"dataGaLocation":877},"https://gitlab.com/users/sign_in/","sign in",[894,923,1023,1028,1142,1198],{"text":895,"config":896,"menu":898},"Platform",{"dataNavLevelOne":897},"platform",{"type":899,"columns":900},"cards",[901,907,915],{"title":895,"description":902,"link":903},"The intelligent orchestration platform for DevSecOps",{"text":904,"config":905},"Explore our Platform",{"href":906,"dataGaName":897,"dataGaLocation":877},"/platform/",{"title":908,"description":909,"link":910},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":911,"config":912},"Meet GitLab Duo",{"href":913,"dataGaName":914,"dataGaLocation":877},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":916,"description":917,"link":918},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":919,"config":920},"Learn more",{"href":921,"dataGaName":922,"dataGaLocation":877},"/why-gitlab/","why gitlab",{"text":924,"left":862,"config":925,"menu":927},"Product",{"dataNavLevelOne":926},"solutions",{"type":928,"link":929,"columns":933,"feature":1002},"lists",{"text":930,"config":931},"View all Solutions",{"href":932,"dataGaName":926,"dataGaLocation":877},"/solutions/",[934,958,981],{"title":935,"description":936,"link":937,"items":942},"Automation","CI/CD and automation to accelerate deployment",{"config":938},{"icon":939,"href":940,"dataGaName":941,"dataGaLocation":877},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[943,947,950,954],{"text":944,"config":945},"CI/CD",{"href":946,"dataGaLocation":877,"dataGaName":944},"/solutions/continuous-integration/",{"text":908,"config":948},{"href":913,"dataGaLocation":877,"dataGaName":949},"gitlab duo agent platform - product menu",{"text":951,"config":952},"Source Code Management",{"href":953,"dataGaLocation":877,"dataGaName":951},"/solutions/source-code-management/",{"text":955,"config":956},"Automated Software Delivery",{"href":940,"dataGaLocation":877,"dataGaName":957},"Automated software delivery",{"title":959,"description":960,"link":961,"items":966},"Security","Deliver code faster without compromising security",{"config":962},{"href":963,"dataGaName":964,"dataGaLocation":877,"icon":965},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[967,971,976],{"text":968,"config":969},"Application Security Testing",{"href":963,"dataGaName":970,"dataGaLocation":877},"Application security testing",{"text":972,"config":973},"Software Supply Chain Security",{"href":974,"dataGaLocation":877,"dataGaName":975},"/solutions/supply-chain/","Software supply chain security",{"text":977,"config":978},"Software Compliance",{"href":979,"dataGaName":980,"dataGaLocation":877},"/solutions/software-compliance/","software compliance",{"title":982,"link":983,"items":988},"Measurement",{"config":984},{"icon":985,"href":986,"dataGaName":987,"dataGaLocation":877},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[989,993,997],{"text":990,"config":991},"Visibility & Measurement",{"href":986,"dataGaLocation":877,"dataGaName":992},"Visibility and Measurement",{"text":994,"config":995},"Value Stream Management",{"href":996,"dataGaLocation":877,"dataGaName":994},"/solutions/value-stream-management/",{"text":998,"config":999},"Analytics & Insights",{"href":1000,"dataGaLocation":877,"dataGaName":1001},"/solutions/analytics-and-insights/","Analytics and insights",{"title":1003,"type":928,"items":1004},"GitLab for",[1005,1011,1017],{"text":1006,"config":1007},"Enterprise",{"icon":1008,"href":1009,"dataGaLocation":877,"dataGaName":1010},"Building","/enterprise/","enterprise",{"text":1012,"config":1013},"Small Business",{"icon":1014,"href":1015,"dataGaLocation":877,"dataGaName":1016},"Work","/small-business/","small business",{"text":1018,"config":1019},"Public Sector",{"icon":1020,"href":1021,"dataGaLocation":877,"dataGaName":1022},"Organization","/solutions/public-sector/","public sector",{"text":1024,"config":1025},"Pricing",{"href":1026,"dataGaName":1027,"dataGaLocation":877,"dataNavLevelOne":1027},"/pricing/","pricing",{"text":1029,"config":1030,"menu":1032},"Resources",{"dataNavLevelOne":1031},"resources",{"type":928,"link":1033,"columns":1037,"feature":1131},{"text":1034,"config":1035},"View all resources",{"href":1036,"dataGaName":1031,"dataGaLocation":877},"/resources/",[1038,1071,1098],{"title":1039,"items":1040},"Getting started",[1041,1046,1051,1056,1061,1066],{"text":1042,"config":1043},"Install",{"href":1044,"dataGaName":1045,"dataGaLocation":877},"/install/","install",{"text":1047,"config":1048},"Quick start guides",{"href":1049,"dataGaName":1050,"dataGaLocation":877},"/get-started/","quick setup checklists",{"text":1052,"config":1053},"Learn",{"href":1054,"dataGaLocation":877,"dataGaName":1055},"https://university.gitlab.com/","learn",{"text":1057,"config":1058},"Product documentation",{"href":1059,"dataGaName":1060,"dataGaLocation":877},"https://docs.gitlab.com/","product documentation",{"text":1062,"config":1063},"Best practice videos",{"href":1064,"dataGaName":1065,"dataGaLocation":877},"/getting-started-videos/","best practice videos",{"text":1067,"config":1068},"Integrations",{"href":1069,"dataGaName":1070,"dataGaLocation":877},"/integrations/","integrations",{"title":1072,"items":1073},"Discover",[1074,1079,1084,1089,1093],{"text":1075,"config":1076},"Customer success stories",{"href":1077,"dataGaName":1078,"dataGaLocation":877},"/customers/","customer success stories",{"text":1080,"config":1081},"Blog",{"href":1082,"dataGaName":1083,"dataGaLocation":877},"/blog/","blog",{"text":1085,"config":1086},"Demo Hub",{"href":1087,"dataGaName":1088,"dataGaLocation":877},"/demo-hub/","demo hub",{"text":1090,"config":1091},"The Source",{"href":1092,"dataGaName":1083,"dataGaLocation":877},"/the-source/",{"text":1094,"config":1095},"Remote",{"href":1096,"dataGaName":1097,"dataGaLocation":877},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1099,"items":1100},"Connect",[1101,1106,1111,1116,1121,1126],{"text":1102,"config":1103},"GitLab Services",{"href":1104,"dataGaName":1105,"dataGaLocation":877},"/services/","services",{"text":1107,"config":1108},"Contribute",{"href":1109,"dataGaName":1110,"dataGaLocation":877},"https://contributors.gitlab.com","contribute",{"text":1112,"config":1113},"Community",{"href":1114,"dataGaName":1115,"dataGaLocation":877},"/community/","community",{"text":1117,"config":1118},"Forum",{"href":1119,"dataGaName":1120,"dataGaLocation":877},"https://forum.gitlab.com/","forum",{"text":1122,"config":1123},"Events",{"href":1124,"dataGaName":1125,"dataGaLocation":877},"/events/","events",{"text":1127,"config":1128},"Partners",{"href":1129,"dataGaName":1130,"dataGaLocation":877},"/partners/","partners",{"config":1132,"title":1135,"text":1136,"link":1137},{"background":1133,"textColor":1134},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":1138,"config":1139},"Read the latest",{"href":1140,"dataGaName":1141,"dataGaLocation":877},"/whats-new/","whats new",{"text":1143,"config":1144,"menu":1146},"Company",{"dataNavLevelOne":1145},"company",{"type":928,"columns":1147},[1148],{"items":1149},[1150,1155,1161,1163,1168,1173,1178,1183,1188,1193],{"text":1151,"config":1152},"About",{"href":1153,"dataGaName":1154,"dataGaLocation":877},"/company/","about",{"text":1156,"config":1157,"footerGa":1160},"Jobs",{"href":1158,"dataGaName":1159,"dataGaLocation":877},"/jobs/","jobs",{"dataGaName":1159},{"text":1122,"config":1162},{"href":1124,"dataGaName":1125,"dataGaLocation":877},{"text":1164,"config":1165},"Leadership",{"href":1166,"dataGaName":1167,"dataGaLocation":877},"/company/team/e-group/","leadership",{"text":1169,"config":1170},"Handbook",{"href":1171,"dataGaName":1172,"dataGaLocation":877},"https://handbook.gitlab.com/","handbook",{"text":1174,"config":1175},"Investor relations",{"href":1176,"dataGaName":1177,"dataGaLocation":877},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1179,"config":1180},"Trust Center",{"href":1181,"dataGaName":1182,"dataGaLocation":877},"/security/","trust center",{"text":1184,"config":1185},"AI Transparency Center",{"href":1186,"dataGaName":1187,"dataGaLocation":877},"/ai-transparency-center/","ai transparency center",{"text":1189,"config":1190},"Newsletter",{"href":1191,"dataGaName":1192,"dataGaLocation":877},"/company/contact/#contact-forms","newsletter",{"text":1194,"config":1195},"Press",{"href":1196,"dataGaName":1197,"dataGaLocation":877},"/press/","press",{"text":1199,"config":1200,"menu":1201},"Contact us",{"dataNavLevelOne":1145},{"type":928,"columns":1202},[1203],{"items":1204},[1205,1210,1215],{"text":1206,"config":1207},"Talk to sales",{"href":1208,"dataGaName":1209,"dataGaLocation":877},"/sales/","talk to sales",{"text":1211,"config":1212},"Support portal",{"href":1213,"dataGaName":1214,"dataGaLocation":877},"https://support.gitlab.com/hc/en-us","support portal",{"text":1216,"config":1217},"Customer portal",{"href":1218,"dataGaName":1219,"dataGaLocation":877},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1221,"login":1222,"suggestions":1229},"Close",{"text":1223,"link":1224},"To search repositories and projects, login to",{"text":1225,"config":1226},"gitlab.com",{"href":891,"dataGaName":1227,"dataGaLocation":1228},"search login","search",{"text":1230,"default":1231},"Suggestions",[1232,1234,1238,1240,1244,1248],{"text":908,"config":1233},{"href":913,"dataGaName":908,"dataGaLocation":1228},{"text":1235,"config":1236},"Code Suggestions (AI)",{"href":1237,"dataGaName":1235,"dataGaLocation":1228},"/solutions/code-suggestions/",{"text":944,"config":1239},{"href":946,"dataGaName":944,"dataGaLocation":1228},{"text":1241,"config":1242},"GitLab on AWS",{"href":1243,"dataGaName":1241,"dataGaLocation":1228},"/partners/technology-partners/aws/",{"text":1245,"config":1246},"GitLab on Google Cloud",{"href":1247,"dataGaName":1245,"dataGaLocation":1228},"/partners/technology-partners/google-cloud-platform/",{"text":1249,"config":1250},"Why GitLab?",{"href":921,"dataGaName":1249,"dataGaLocation":1228},{"freeTrial":1252,"mobileIcon":1257,"desktopIcon":1262,"secondaryButton":1265},{"text":1253,"config":1254},"Start free trial",{"href":1255,"dataGaName":882,"dataGaLocation":1256},"https://gitlab.com/-/trials/new/","nav",{"altText":1258,"config":1259},"Gitlab Icon",{"src":1260,"dataGaName":1261,"dataGaLocation":1256},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1258,"config":1263},{"src":1264,"dataGaName":1261,"dataGaLocation":1256},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1266,"config":1267},"Get Started",{"href":1268,"dataGaName":1269,"dataGaLocation":1256},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1271,"mobileIcon":1275,"desktopIcon":1277},{"text":1272,"config":1273},"Learn more about GitLab Duo",{"href":913,"dataGaName":1274,"dataGaLocation":1256},"gitlab duo",{"altText":1258,"config":1276},{"src":1260,"dataGaName":1261,"dataGaLocation":1256},{"altText":1258,"config":1278},{"src":1264,"dataGaName":1261,"dataGaLocation":1256},{"button":1280,"mobileIcon":1285,"desktopIcon":1287},{"text":1281,"config":1282},"/switch",{"href":1283,"dataGaName":1284,"dataGaLocation":1256},"#contact","switch",{"altText":1258,"config":1286},{"src":1260,"dataGaName":1261,"dataGaLocation":1256},{"altText":1258,"config":1288},{"src":1289,"dataGaName":1261,"dataGaLocation":1256},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1291,"mobileIcon":1296,"desktopIcon":1298},{"text":1292,"config":1293},"Back to pricing",{"href":1026,"dataGaName":1294,"dataGaLocation":1256,"icon":1295},"back to pricing","GoBack",{"altText":1258,"config":1297},{"src":1260,"dataGaName":1261,"dataGaLocation":1256},{"altText":1258,"config":1299},{"src":1264,"dataGaName":1261,"dataGaLocation":1256},{"title":1301,"titleMobile":1302,"button":1303,"config":1308},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":919,"config":1304},{"href":1305,"dataGaName":1306,"dataGaLocation":1307},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":508,"disabled":837},{"data":1310},{"text":1311,"source":1312,"edit":1318,"contribute":1323,"config":1328,"items":1333,"minimal":1543},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1313,"config":1314},"View page source",{"href":1315,"dataGaName":1316,"dataGaLocation":1317},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1319,"config":1320},"Edit this page",{"href":1321,"dataGaName":1322,"dataGaLocation":1317},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1324,"config":1325},"Please contribute",{"href":1326,"dataGaName":1327,"dataGaLocation":1317},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1329,"facebook":1330,"youtube":1331,"linkedin":1332},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1334,1381,1435,1479,1511],{"title":1024,"links":1335,"subMenu":1350},[1336,1340,1345],{"text":1337,"config":1338},"View plans",{"href":1026,"dataGaName":1339,"dataGaLocation":1317},"view plans",{"text":1341,"config":1342},"Why Premium?",{"href":1343,"dataGaName":1344,"dataGaLocation":1317},"/pricing/premium/","why premium",{"text":1346,"config":1347},"Why Ultimate?",{"href":1348,"dataGaName":1349,"dataGaLocation":1317},"/pricing/ultimate/","why ultimate",[1351],{"title":1352,"links":1353},"Contact Us",[1354,1357,1359,1361,1366,1371,1376],{"text":1355,"config":1356},"Contact sales",{"href":1208,"dataGaName":887,"dataGaLocation":1317},{"text":1211,"config":1358},{"href":1213,"dataGaName":1214,"dataGaLocation":1317},{"text":1216,"config":1360},{"href":1218,"dataGaName":1219,"dataGaLocation":1317},{"text":1362,"config":1363},"Status",{"href":1364,"dataGaName":1365,"dataGaLocation":1317},"https://status.gitlab.com/","status",{"text":1367,"config":1368},"Terms of use",{"href":1369,"dataGaName":1370,"dataGaLocation":1317},"/terms/","terms of use",{"text":1372,"config":1373},"Privacy statement",{"href":1374,"dataGaName":1375,"dataGaLocation":1317},"/privacy/","privacy statement",{"text":1377,"config":1378},"Cookie preferences",{"dataGaName":1379,"dataGaLocation":1317,"id":1380,"isOneTrustButton":862},"cookie preferences","ot-sdk-btn",{"title":924,"links":1382,"subMenu":1391},[1383,1387],{"text":1384,"config":1385},"DevSecOps platform",{"href":906,"dataGaName":1386,"dataGaLocation":1317},"devsecops platform",{"text":1388,"config":1389},"AI-Assisted Development",{"href":913,"dataGaName":1390,"dataGaLocation":1317},"ai-assisted development",[1392],{"title":1393,"links":1394},"Topics",[1395,1400,1405,1410,1415,1420,1425,1430],{"text":1396,"config":1397},"CICD",{"href":1398,"dataGaName":1399,"dataGaLocation":1317},"/topics/ci-cd/","cicd",{"text":1401,"config":1402},"GitOps",{"href":1403,"dataGaName":1404,"dataGaLocation":1317},"/topics/gitops/","gitops",{"text":1406,"config":1407},"DevOps",{"href":1408,"dataGaName":1409,"dataGaLocation":1317},"/topics/devops/","devops",{"text":1411,"config":1412},"Version Control",{"href":1413,"dataGaName":1414,"dataGaLocation":1317},"/topics/version-control/","version control",{"text":1416,"config":1417},"DevSecOps",{"href":1418,"dataGaName":1419,"dataGaLocation":1317},"/topics/devsecops/","devsecops",{"text":1421,"config":1422},"Cloud Native",{"href":1423,"dataGaName":1424,"dataGaLocation":1317},"/topics/cloud-native/","cloud native",{"text":1426,"config":1427},"AI for Coding",{"href":1428,"dataGaName":1429,"dataGaLocation":1317},"/topics/devops/ai-for-coding/","ai for coding",{"text":1431,"config":1432},"Agentic AI",{"href":1433,"dataGaName":1434,"dataGaLocation":1317},"/topics/agentic-ai/","agentic ai",{"title":1436,"links":1437},"Solutions",[1438,1440,1442,1447,1451,1454,1458,1461,1463,1466,1469,1474],{"text":968,"config":1439},{"href":963,"dataGaName":968,"dataGaLocation":1317},{"text":957,"config":1441},{"href":940,"dataGaName":941,"dataGaLocation":1317},{"text":1443,"config":1444},"Agile development",{"href":1445,"dataGaName":1446,"dataGaLocation":1317},"/solutions/agile-delivery/","agile delivery",{"text":1448,"config":1449},"SCM",{"href":953,"dataGaName":1450,"dataGaLocation":1317},"source code management",{"text":1396,"config":1452},{"href":946,"dataGaName":1453,"dataGaLocation":1317},"continuous integration & delivery",{"text":1455,"config":1456},"Value stream management",{"href":996,"dataGaName":1457,"dataGaLocation":1317},"value stream management",{"text":1401,"config":1459},{"href":1460,"dataGaName":1404,"dataGaLocation":1317},"/solutions/gitops/",{"text":1006,"config":1462},{"href":1009,"dataGaName":1010,"dataGaLocation":1317},{"text":1464,"config":1465},"Small business",{"href":1015,"dataGaName":1016,"dataGaLocation":1317},{"text":1467,"config":1468},"Public sector",{"href":1021,"dataGaName":1022,"dataGaLocation":1317},{"text":1470,"config":1471},"Education",{"href":1472,"dataGaName":1473,"dataGaLocation":1317},"/solutions/education/","education",{"text":1475,"config":1476},"Financial services",{"href":1477,"dataGaName":1478,"dataGaLocation":1317},"/solutions/finance/","financial services",{"title":1029,"links":1480},[1481,1483,1485,1487,1490,1492,1495,1497,1499,1501,1503,1505,1507,1509],{"text":1042,"config":1482},{"href":1044,"dataGaName":1045,"dataGaLocation":1317},{"text":1047,"config":1484},{"href":1049,"dataGaName":1050,"dataGaLocation":1317},{"text":1052,"config":1486},{"href":1054,"dataGaName":1055,"dataGaLocation":1317},{"text":1057,"config":1488},{"href":1059,"dataGaName":1489,"dataGaLocation":1317},"docs",{"text":1080,"config":1491},{"href":1082,"dataGaName":1083,"dataGaLocation":1317},{"text":1493,"config":1494},"What's new",{"href":1140,"dataGaName":1141,"dataGaLocation":1317},{"text":1075,"config":1496},{"href":1077,"dataGaName":1078,"dataGaLocation":1317},{"text":1094,"config":1498},{"href":1096,"dataGaName":1097,"dataGaLocation":1317},{"text":1102,"config":1500},{"href":1104,"dataGaName":1105,"dataGaLocation":1317},{"text":1107,"config":1502},{"href":1109,"dataGaName":1110,"dataGaLocation":1317},{"text":1112,"config":1504},{"href":1114,"dataGaName":1115,"dataGaLocation":1317},{"text":1117,"config":1506},{"href":1119,"dataGaName":1120,"dataGaLocation":1317},{"text":1122,"config":1508},{"href":1124,"dataGaName":1125,"dataGaLocation":1317},{"text":1127,"config":1510},{"href":1129,"dataGaName":1130,"dataGaLocation":1317},{"title":1143,"links":1512},[1513,1515,1517,1519,1521,1523,1527,1532,1534,1536,1538],{"text":1151,"config":1514},{"href":1153,"dataGaName":1145,"dataGaLocation":1317},{"text":1156,"config":1516},{"href":1158,"dataGaName":1159,"dataGaLocation":1317},{"text":1164,"config":1518},{"href":1166,"dataGaName":1167,"dataGaLocation":1317},{"text":1169,"config":1520},{"href":1171,"dataGaName":1172,"dataGaLocation":1317},{"text":1174,"config":1522},{"href":1176,"dataGaName":1177,"dataGaLocation":1317},{"text":1524,"config":1525},"Sustainability",{"href":1526,"dataGaName":1524,"dataGaLocation":1317},"/sustainability/",{"text":1528,"config":1529},"Diversity, inclusion and belonging (DIB)",{"href":1530,"dataGaName":1531,"dataGaLocation":1317},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1179,"config":1533},{"href":1181,"dataGaName":1182,"dataGaLocation":1317},{"text":1189,"config":1535},{"href":1191,"dataGaName":1192,"dataGaLocation":1317},{"text":1194,"config":1537},{"href":1196,"dataGaName":1197,"dataGaLocation":1317},{"text":1539,"config":1540},"Modern Slavery Transparency Statement",{"href":1541,"dataGaName":1542,"dataGaLocation":1317},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1544},[1545,1548,1551],{"text":1546,"config":1547},"Terms",{"href":1369,"dataGaName":1370,"dataGaLocation":1317},{"text":1549,"config":1550},"Cookies",{"dataGaName":1379,"dataGaLocation":1317,"id":1380,"isOneTrustButton":862},{"text":1552,"config":1553},"Privacy",{"href":1374,"dataGaName":1375,"dataGaLocation":1317},[1555],{"id":1556,"title":7,"body":836,"config":1557,"content":1559,"description":836,"extension":1563,"meta":1564,"navigation":862,"path":1565,"seo":1566,"stem":1567,"__hash__":1568},"blogAuthors/en-us/blog/authors/alexander-malaev.yml",{"template":1558},"BlogAuthor",{"name":7,"config":1560},{"headshot":1561,"ctfId":1562},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","Alexander-Malaev","yml",{},"/en-us/blog/authors/alexander-malaev",{},"en-us/blog/authors/alexander-malaev","KqBKzxSUA1i552dD3yr38zH3AoyBHn92z6737WFCJhg",[1570,1579,1587],{"title":1571,"description":1572,"heroImage":1573,"category":832,"date":1574,"authors":1575,"slug":1578,"externalUrl":836},"Confidential AI for GitLab Self-Hosted","Give developers AI coding agents in GitLab Duo without source code leaving a hardware-encrypted boundary — no GPUs needed.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-08-06",[1576,1577],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1580,"description":1581,"heroImage":1582,"category":832,"date":1583,"authors":1584,"slug":1586,"externalUrl":836},"Green DevOps: Why carbon measurement belongs in your CI/CD pipeline","CI/CD pipelines have a hidden carbon cost. Here's why measuring it matters, and how you can get started with Eco CI and Carmen in GitLab.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-07-09",[1585],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1588,"description":1589,"heroImage":1590,"category":832,"date":1591,"authors":1592,"slug":1594,"externalUrl":836},"How to build CI/CD observability at scale","This practical guide to GitLab pipeline analytics helps self-managed users gain operational insights using Prometheus and Grafana.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28",[1593],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1596},[1597,1611,1623,1635],{"id":1598,"categories":1599,"header":1601,"text":1602,"button":1603,"image":1608},"ai-modernization",[1600],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1604,"config":1605},"Get your AI maturity score",{"href":1606,"dataGaName":1607,"dataGaLocation":1083},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1609},{"src":1610},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1612,"categories":1613,"header":1615,"text":1602,"button":1616,"image":1620},"devops-modernization",[1614,1419],"product","Are you just managing tools or shipping innovation?",{"text":1617,"config":1618},"Get your DevOps maturity score",{"href":1619,"dataGaName":1607,"dataGaLocation":1083},"/assessments/devops-modernization-assessment/",{"config":1621},{"src":1622},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1624,"categories":1625,"header":1627,"text":1602,"button":1628,"image":1632},"security-modernization",[1626],"security","Are you trading speed for security?",{"text":1629,"config":1630},"Get your security maturity score",{"href":1631,"dataGaName":1607,"dataGaLocation":1083},"/assessments/security-modernization-assessment/",{"config":1633},{"src":1634},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1636,"paths":1637,"header":1640,"text":1641,"button":1642,"image":1647},"github-azure-migration",[1638,1639],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1643,"config":1644},"See how GitLab compares to GitHub",{"href":1645,"dataGaName":1646,"dataGaLocation":1083},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1648},{"src":1622},{"header":1650,"blurb":1651,"button":1652,"secondaryButton":1657},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1653,"config":1654},"Get your free trial",{"href":1655,"dataGaName":882,"dataGaLocation":1656},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1355,"config":1658},{"href":1208,"dataGaName":887,"dataGaLocation":1656},1786803747902]