CINXE.COM

Installing a GitLab POC on Amazon Web Services (AWS) | GitLab Docs

<!doctype html><html lang=en-US dir=ltr><head><script src=https://cdn.cookielaw.org/consent/7f944245-c5cd-4eed-a90e-dd955adfdd08/OtAutoBlock.js></script><script src=https://cdn.cookielaw.org/scripttemplates/otSDKStub.js data-domain-script=7f944245-c5cd-4eed-a90e-dd955adfdd08></script><script type=text/javascript>function OptanonWrapper(){}</script><script>const callback=(e)=>{for(const t of e)t.type==="childList"&&t.addedNodes.forEach(e=>{e.nodeName==="IMG"&&document.querySelectorAll('img:not([src^="http"]):not([data-ot-ignore])').forEach(e=>{e.setAttribute("data-ot-ignore","")})})},config={attributes:!0,childList:!0,subtree:!0,attributeFilter:["src"]},observer=new MutationObserver(callback);observer.observe(document.documentElement,config)</script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","GTM-NJXWQL"),gtag("consent","default",{analytics_storage:"granted",ad_storage:"granted",functionality_storage:"granted",wait_for_update:500}),gtag("consent","default",{analytics_storage:"denied",ad_storage:"denied",functionality_storage:"denied",region:["AT","BE","BG","HR","CY","CZ","DK","EE","FI","FR","DE","GR","HU","IE","IT","LV","LT","LU","MT","NL","PL","PT","RO","SK","SI","ES","SE","IS","LI","NO","GB","PE","RU"],wait_for_update:500}),window.geofeed=e=>{dataLayer.push({event:"OneTrustCountryLoad",oneTrustCountryId:e.country.toString()})};const json=document.createElement("script");json.setAttribute("src","https://geolocation.onetrust.com/cookieconsentpub/v1/geo/location/geofeed"),document.head.appendChild(json)</script><meta charset=utf-8><meta name=viewport content="width=device-width"><title>Installing a GitLab POC on Amazon Web Services (AWS) | GitLab Docs</title> <link rel=icon href=/favicon.ico sizes=any><link rel=icon href=/favicon.svg type=image/svg+xml><link rel=apple-touch-icon href=/apple-touch-icon.png><link rel=manifest href=/manifests/manifest.webmanifest><meta name=theme-color content="#FC6D26"><link rel=canonical href=https://docs.gitlab.com/install/aws/><meta name=description content="Read through the GitLab installation methods."><link rel=preload href=/gitlab_ui/fonts/GitLabSans.woff2 type=font/woff2 as=font crossorigin><link rel=prefetch href=/gitlab_ui/fonts/GitLabSans-Italic.woff2 crossorigin><link rel=prefetch href=/gitlab_ui/fonts/GitLabMono.woff2 crossorigin><link rel=stylesheet href=/gitlab_ui/ui/index.css><link rel=stylesheet href=/vite/main.css><meta name=gitlab_docs_base_url content="/"><meta class=elastic name=gitlab_docs_version content="17.11"><meta class=elastic name=gitlab_docs_section content="install"><meta class=elastic name=gitlab_docs_breadcrumbs content="Install › Cloud providers"><meta name=gitlab_docs_legacy_path content="/ee/install/aws/index.html"><meta name=gitlab_docs_hugo_launch_version content="17.9"><script>const ELASTIC_KEY="cDFpLWJaSUJXVHBqWWI4VGZKN3M6eENBSjl4WDRSRnlCUW94ajRQazhLQQ==",ELASTIC_INDEX="search-gitlab-docs-hugo"</script><script type=module src=/vite/elastic_search.js></script><script type=module src=/vite/history.js></script><meta name=google-site-verification content="AcGSBNaKDWnLgcYotlVibGy6STm2Y6_KJSaRxrA90xY"><meta name=google-site-verification content="6eFQOFLxYAer08ROqc3I-SAi44F9NmvH7PrUUBR3oCI"><meta name=google-site-verification content="xAUTWp3CDg-tU1LVVwsM9OrVhLR7L3SmiyKzkOuPNos"><meta name=google-site-verification content="F0zzwaMpiyWFcPQ1Lqu18qN3EnuQsqFXbySl_29yvHs"><meta name=google-site-verification content="nwo1bVaU0t9TZxZyM-aOI6-CofaH9GRL-uBPbdREWgc"><meta name=google-site-verification content="rWoHrtHEmIX0t28oOb1ZEDMYZb_EZA6rr6ZOl5otEPI"><meta name=google-site-verification content="fSxr8-uslxcuFL0N-oECp3Tm0RPNEGX97wbdayKOEL8"><meta name=google-site-verification content="ISxyLVnZqU8oY3jwrK7EO9o-2DOTvLJwPse7bZz6yhs"><meta name=google-site-verification content="x1WspIvz3ZHqS0gezfX_P-qiRDOeP2Oyrd68zrU2ErI"><meta name=google-site-verification content="94tkqWSqC1gAkWpsWgOA0l908EXJz_ncu794v5XjpWs"><meta name=google-site-verification content="DfXB2Za52GT3zs_vuLIAL4Mi3M3K4qxXcg7MAs0CUqo"><meta name=google-site-verification content="BCEBC2LC7A1NzO9Com1oBrWK88tV_QXfUL0i9mwXPL0"><meta name=google-site-verification content="a2lNcHMorfS43aoISjZt5_BBPo-H1UaTKMQdBgZO9iY"><meta name=google-site-verification content="0s16pP9MelY6wDHRf-izXb5pwLU01IogP-Uc_e8f3GU"><meta name=google-site-verification content="H474RNof35Xp8fLg02fZbg9Dzxdtfch6vtcjzpmUraU"><meta name=google-site-verification content="E0FlhpgBGeE7d1pQ6amdcIWPMDLDeu15-HLQVoDTguE"><meta name=google-site-verification content="opQd7_rXtPy-pX5CO_XZiztzeQEsXnB3j6Y1_dZAizA"><meta name=google-site-verification content="06Kq4AoXdmBOjOAkbPvnYGtSxnn4Q9QBqEO55PLlw5c"><meta name=google-site-verification content="djBBokRFSWV_VRlSE51V5TZSPzMC6hml5l-Sb22WglE"><meta name=google-site-verification content="UOW6nOsvbyMeIySuamzbws4kNC_WqehamWfoxxtKjZ8"><meta name=google-site-verification content="hXU1Gsdba74DUbvbdUHRl9o0cQeiwXIhAdIllOG6p8E"><meta name=google-site-verification content="YFeHIAPk9lE76ubVMeq4P0sQVnzo2-a4k1oU_bPY8yE"><meta name=google-site-verification content="h8ICI4eDkvXmYaGDuLTLoWuXnLn-KUkChqYB-roMRsw"><meta name=zd-site-verification content="ony3w7hk1vs6tfyrc51mld"><meta name=zd-site-verification content="gtuq65qdzt6n31viazi6hj"></head><body data-elastic-exclude><nav data-elastic-exclude class="header gl-w-full gl-fixed gl-py-0 gl-px-5 gl-z-4 gl-bg-theme-indigo-900 gl-flex gl-justify-between gl-items-center"><a href=#skipTarget class="gl-sr-only skip-link">Skip to main content</a> <a class="header-logo gl-flex lg:gl-mr-5" href=/><img src=/gitlab-logo-header.svg alt="GitLab documentation home" class=logo> <span class="border-light gl-ml-3 gl-pl-3 gl-py-1 gl-border-0 gl-border-l gl-border-solid gl-border-gray-100 gl-align-middle gl-text-white">Docs</span> </a><button class="lg:gl-hidden gl-mt-5 gl-border-0 navbar-toggle" aria-label="Toggle navbar" data-toggle=collapse data-target=.header-right></button><div class="mobile-header gl-w-full gl-text-base"><div class="header-right collapse md:gl-mt-3 lg:gl-mt-0"><div class="js-elastic-search-form gl-spinner-container"><span role=status aria-label=Loading class="gl-ml-3 gl-align-text-bottom! gl-spinner gl-spinner-light gl-spinner-sm"></span></div><div class="gl-flex gl-flex-col lg:gl-flex-row lg:gl-items-center gl-mb-0"><a class="!gl-text-white gl-mr-5 gl-w-fit md:gl-mt-4 lg:gl-mt-0" href=https://about.gitlab.com/releases/categories/releases/ target=_blank rel="noopener noreferrer">What's new?</a><div data-vue-app=versions-menu></div><a class="cta-button gl-my-3 lg:gl-my-0" href="https://gitlab.com/-/trial_registrations/new?glm_source=docs.gitlab.com&amp;amp;glm_content=navigation-cta-docs" target=_blank rel="noopener noreferrer" role=button>Get free trial</a></div></div></div></nav><main><div id=js-version-banner></div><div class=template-single><div data-vue-app=sidebar-menu></div><div data-pagefind-body class=main-content><div class="docs-content gl-overflow-y-auto gl-pb-7"><h1 id=skipTarget class="gl-mt-5 lg:gl-mt-8 gl-mb-6">Installing a GitLab POC on Amazon Web Services (AWS)</h1><div data-elastic-include><div class="availability gl-text-base gl-pl-4 gl-mb-5"><ul class="gl-list-none gl-p-0 gl-m-0"><li><span class=gl-font-bold>Tier</span>: Free, Premium, Ultimate</li><li><span class=gl-font-bold>Offering</span>: GitLab Self-Managed</li></ul></div><p>This page offers a walkthrough of a common configuration for GitLab on AWS using the official Linux package. You should customize it to accommodate your needs.</p><div class="alert alert-type-note"><p>For organizations with 1,000 users or less, the recommended AWS installation method is to launch an EC2 single box <a href=https://about.gitlab.com/install/>Linux package installation</a> and implement a snapshot strategy for backing up the data. See the <a href=/administration/reference_architectures/1k_users/>20 RPS or 1,000 user reference architecture</a> for more information.</p></div><h2 id=getting-started-for-production-grade-gitlab>Getting started for production-grade GitLab</h2><div class="alert alert-type-note"><p>This document is an installation guide for a proof of concept instance. It is not a reference architecture, and it does not result in a highly available configuration. It&rsquo;s highly recommended to use the <a href=https://gitlab.com/gitlab-org/gitlab-environment-toolkit>GitLab Environment Toolkit (GET)</a> instead.</p></div><p>Following this guide exactly results in a proof of concept instance that roughly equates to a <strong>scaled down</strong> version of a <strong>two availability zone implementation</strong> of the <strong>Non-HA</strong> <a href=/administration/reference_architectures/2k_users/>40 RPS or 2,000 User Reference Architecture</a>. The 2K reference architecture is not HA because it is primarily intended to provide some scaling while keeping costs and complexity low. The <a href=/administration/reference_architectures/3k_users/>60 RPS or 3,000 User Reference Architecture</a> is the smallest size that is GitLab HA. It has additional service roles to achieve HA, most notably it uses Gitaly Cluster to achieve HA for Git repository storage and specifies triple redundancy.</p><p>GitLab maintains and tests two main types of Reference Architectures. The <strong>Linux package architectures</strong> are implemented on instance compute while <strong>Cloud Native Hybrid architectures</strong> maximize the use of a Kubernetes cluster. Cloud Native Hybrid reference architecture specifications are addendum sections to the Reference Architecture size pages that start by describing the Linux package architecture. For example, the 60 RPS or 3,000 User Cloud Native Reference Architecture is in the subsection titled <a href=/administration/reference_architectures/3k_users/#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative>Cloud Native Hybrid reference architecture with Helm Charts (alternative)</a> in the 60 RPS or 3,000 User Reference Architecture page.</p><h3 id=getting-started-for-production-grade-linux-package-installations>Getting started for production-grade Linux package installations</h3><p>The Infrastructure as Code tooling <a href=https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/tree/main>GitLab Environment Tool (GET)</a> is the best place to start for building using the Linux package on AWS and most especially if you are targeting an HA setup. While it does not automate everything, it does complete complex setups like Gitaly Cluster for you. GET is open source so anyone can build on top of it and contribute improvements to it.</p><h3 id=getting-started-for-production-grade-cloud-native-hybrid-gitlab>Getting started for production-grade Cloud Native Hybrid GitLab</h3><p>The <a href=https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/README.md>GitLab Environment Toolkit (GET)</a> is a set of opinionated Terraform and Ansible scripts. These scripts help with the deployment of Linux package or Cloud Native Hybrid environments on selected cloud providers and are used by GitLab developers for <a href=/subscriptions/gitlab_dedicated/>GitLab Dedicated</a> (for example).</p><p>You can use the GitLab Environment Toolkit to deploy a Cloud Native Hybrid environment on AWS. However, it&rsquo;s not required and may not support every valid permutation. That said, the scripts are presented as-is and you can adapt them accordingly.</p><h2 id=introduction>Introduction</h2><p>For the most part, we make use of the Linux package in our setup, but we also leverage native AWS services. Instead of using the Linux package-bundled PostgreSQL and Redis, we use Amazon RDS and ElastiCache.</p><p>In this guide, we go through a multi-node setup where we start by configuring our Virtual Private Cloud and subnets to later integrate services such as RDS for our database server and ElastiCache as a Redis cluster to finally manage them in an auto scaling group with custom scaling policies.</p><h2 id=requirements>Requirements</h2><p>In addition to having a basic familiarity with <a href=https://docs.aws.amazon.com/>AWS</a> and <a href=https://docs.aws.amazon.com/ec2/>Amazon EC2</a>, you need:</p><ul><li><a href=https://console.aws.amazon.com/console/home>An AWS account</a></li><li><a href=https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html>To create or upload an SSH key</a> to connect to the instance via SSH</li><li>A domain name for the GitLab instance</li><li>An SSL/TLS certificate to secure your domain. If you do not already own one, you can provision a free public SSL/TLS certificate through <a href=https://aws.amazon.com/certificate-manager/>AWS Certificate Manager</a>(ACM) for use with the <a href=/install/aws/#load-balancer>Elastic Load Balancer</a> we create.</li></ul><div class="alert alert-type-note"><p>It can take a few hours to validate a certificate provisioned through ACM. To avoid delays later, request your certificate as soon as possible.</p></div><h2 id=architecture>Architecture</h2><p>Below is a diagram of the recommended architecture.</p><p><a href=/install/aws/img/aws_ha_architecture_diagram_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Scaled down 2 Availability Zone Non-HA AWS architecture" src=/install/aws/img/aws_ha_architecture_diagram_v17_0.png></a></p><h2 id=aws-costs>AWS costs</h2><p>GitLab uses the following AWS services, with links to pricing information:</p><ul><li><strong>EC2</strong>: GitLab is deployed on shared hardware, for which <a href=https://aws.amazon.com/ec2/pricing/on-demand/>on-demand pricing</a> applies. If you want to run GitLab on a dedicated or reserved instance, see the <a href=https://aws.amazon.com/ec2/pricing/>EC2 pricing page</a> for information about its cost.</li><li><strong>S3</strong>: GitLab uses S3 (<a href=https://aws.amazon.com/s3/pricing/>pricing page</a>) to store backups, artifacts, and LFS objects.</li><li><strong>NLB</strong>: A Network Load Balancer (<a href=https://aws.amazon.com/elasticloadbalancing/pricing/>pricing page</a>), used to route requests to the GitLab instances.</li><li><strong>RDS</strong>: An Amazon Relational Database Service using PostgreSQL (<a href=https://aws.amazon.com/rds/postgresql/pricing/>pricing page</a>).</li><li><strong>ElastiCache</strong>: An in-memory cache environment (<a href=https://aws.amazon.com/elasticache/pricing/>pricing page</a>), used to provide a Redis configuration.</li></ul><h2 id=create-an-iam-ec2-instance-role-and-profile>Create an IAM EC2 instance role and profile</h2><p>As we are using <a href=/install/aws/#amazon-s3-object-storage>Amazon S3 object storage</a>, our EC2 instances must have read, write, and list permissions for our S3 buckets. To avoid embedding AWS keys in our GitLab configuration, we make use of an <a href=https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html>IAM Role</a> to allow our GitLab instance with this access. We must create an IAM policy to attach to our IAM role:</p><h3 id=create-an-iam-policy>Create an IAM Policy</h3><ol><li><p>Go to the IAM dashboard and select <strong>Policies</strong> in the left menu.</p></li><li><p>Select <strong>Create policy</strong>, select the <code>JSON</code> tab, and add a policy. We want to <a href=https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege>follow security best practices and grant <em>least privilege</em></a>, giving our role only the permissions needed to perform the required actions.</p><ol><li>Assuming you prefix the S3 bucket names with <code>gl-</code> as shown in the diagram, add the following policy:</li></ol><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=json class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ <span style=color:green;font-weight:700>&#34;Version&#34;</span>: <span style=color:#ba2121>&#34;2012-10-17&#34;</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>&#34;Statement&#34;</span>: [ </span></span><span style=display:flex><span> { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>&#34;Effect&#34;</span>: <span style=color:#ba2121>&#34;Allow&#34;</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>&#34;Action&#34;</span>: [ </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:PutObject&#34;</span>, </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:GetObject&#34;</span>, </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:DeleteObject&#34;</span>, </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:PutObjectAcl&#34;</span> </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>&#34;Resource&#34;</span>: <span style=color:#ba2121>&#34;arn:aws:s3:::gl-*/*&#34;</span> </span></span><span style=display:flex><span> }, </span></span><span style=display:flex><span> { </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>&#34;Effect&#34;</span>: <span style=color:#ba2121>&#34;Allow&#34;</span>, </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>&#34;Action&#34;</span>: [ </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:ListBucket&#34;</span>, </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:AbortMultipartUpload&#34;</span>, </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:ListMultipartUploadParts&#34;</span>, </span></span><span style=display:flex><span> <span style=color:#ba2121>&#34;s3:ListBucketMultipartUploads&#34;</span> </span></span><span style=display:flex><span> ], </span></span><span style=display:flex><span> <span style=color:green;font-weight:700>&#34;Resource&#34;</span>: <span style=color:#ba2121>&#34;arn:aws:s3:::gl-*&#34;</span> </span></span><span style=display:flex><span> } </span></span><span style=display:flex><span> ] </span></span><span style=display:flex><span>}</span></span></code></pre></div></div></li><li><p>Select <strong>Next</strong> to review the policy. Give your policy a name (we use <code>gl-s3-policy</code>), and select <strong>Create policy</strong>.</p></li></ol><h3 id=create-an-iam-role>Create an IAM Role</h3><ol><li>Still on the IAM dashboard, select <strong>Roles</strong> in the left menu, and select <strong>Create role</strong>.</li><li>For the <strong>Trusted entity type</strong>, select <code>AWS service</code>. For the <strong>Use case</strong>, select <code>EC2</code> for both the dropdown list and radio buttons and select <strong>Next</strong>.</li><li>In the policy filter, search for the <code>gl-s3-policy</code> we created above, select it, and select <strong>Next</strong>.</li><li>Give the role a name (we use <code>GitLabS3Access</code>). If required, add some tags. Select <strong>Create role</strong>.</li></ol><p>We use this role when we <a href=/install/aws/#create-a-launch-template>create a launch template</a> later on.</p><h2 id=configuring-the-network>Configuring the network</h2><p>We start by creating a VPC for our GitLab cloud infrastructure, then we can create subnets to have public and private instances in at least two <a href=https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html>Availability Zones (AZs)</a>. Public subnets require a Route Table keep and an associated Internet Gateway.</p><h3 id=creating-the-virtual-private-cloud-vpc>Creating the Virtual Private Cloud (VPC)</h3><p>We now create a VPC, a virtual networking environment that you control:</p><ol><li><p>Sign in to <a href=https://console.aws.amazon.com/vpc/home>Amazon Web Services</a>.</p></li><li><p>Select <strong>Your VPCs</strong> from the left menu and then select <strong>Create VPC</strong>. At the &ldquo;Name tag&rdquo; enter <code>gitlab-vpc</code> and at the &ldquo;IPv4 CIDR block&rdquo; enter <code>10.0.0.0/16</code>. If you don&rsquo;t require dedicated hardware, you can leave &ldquo;Tenancy&rdquo; as default. Select <strong>Create VPC</strong> when ready.</p><p><a href=/install/aws/img/create_vpc_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Create a VPC for GitLab cloud infrastructure" src=/install/aws/img/create_vpc_v17_0.png></a></p></li><li><p>Select the VPC, select <strong>Actions</strong>, select <strong>Edit VPC Settings</strong> and check <strong>Enable DNS resolution</strong>. Select <strong>Save</strong> when done.</p></li></ol><h3 id=subnets>Subnets</h3><p>Now, let&rsquo;s create some subnets in different Availability Zones. Make sure that each subnet is associated to the VPC we just created and that CIDR blocks don&rsquo;t overlap. This also allows us to enable multi AZ for redundancy.</p><p>We create private and public subnets to match load balancers and RDS instances as well:</p><ol><li><p>Select <strong>Subnets</strong> from the left menu.</p></li><li><p>Select <strong>Create subnet</strong>. Give it a descriptive name tag based on the IP, for example <code>gitlab-public-10.0.0.0</code>, select the VPC we created previously, select an availability zone (we use <code>us-west-2a</code>), and at the IPv4 CIDR block let&rsquo;s give it a 24 subnet <code>10.0.0.0/24</code>:</p><p><a href=/install/aws/img/create_subnet_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Create subnet" src=/install/aws/img/create_subnet_v17_0.png></a></p></li><li><p>Follow the same steps to create all subnets:</p><table><thead><tr><th>Name tag</th><th>Type</th><th>Availability Zone</th><th>CIDR block</th></tr></thead><tbody><tr><td><code>gitlab-public-10.0.0.0</code></td><td>public</td><td><code>us-west-2a</code></td><td><code>10.0.0.0/24</code></td></tr><tr><td><code>gitlab-private-10.0.1.0</code></td><td>private</td><td><code>us-west-2a</code></td><td><code>10.0.1.0/24</code></td></tr><tr><td><code>gitlab-public-10.0.2.0</code></td><td>public</td><td><code>us-west-2b</code></td><td><code>10.0.2.0/24</code></td></tr><tr><td><code>gitlab-private-10.0.3.0</code></td><td>private</td><td><code>us-west-2b</code></td><td><code>10.0.3.0/24</code></td></tr></tbody></table></li><li><p>Once all the subnets are created, enable <strong>Auto-assign IPv4</strong> for the two public subnets:</p><ol><li>Select each public subnet in turn, select <strong>Actions</strong>, and select <strong>Edit subnet settings</strong>. Check the <strong>Enable auto-assign public IPv4 address</strong> option and save.</li></ol></li></ol><h3 id=internet-gateway>Internet Gateway</h3><p>Now, still on the same dashboard, go to Internet Gateways and create a new one:</p><ol><li><p>Select <strong>Internet Gateways</strong> from the left menu.</p></li><li><p>Select <strong>Create internet gateway</strong>, give it the name <code>gitlab-gateway</code> and select <strong>Create</strong>.</p></li><li><p>Select it from the table, and then under the <strong>Actions</strong> dropdown list choose &ldquo;Attach to VPC&rdquo;.</p><p><a href=/install/aws/img/create_gateway_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Create an internet gateway" src=/install/aws/img/create_gateway_v17_0.png></a></p></li><li><p>Choose <code>gitlab-vpc</code> from the list and hit <strong>Attach</strong>.</p></li></ol><h3 id=create-nat-gateways>Create NAT Gateways</h3><p>Instances deployed in our private subnets must connect to the internet for updates, but should not be reachable from the public internet. To achieve this, we make use of <a href=https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html>NAT Gateways</a> deployed in each of our public subnets:</p><ol><li>Go to the VPC dashboard and select <strong>NAT Gateways</strong> in the left menu bar.</li><li>Select <strong>Create NAT Gateway</strong> and complete the following:<ol><li><strong>Subnet</strong>: Select <code>gitlab-public-10.0.0.0</code> from the dropdown list.</li><li><strong>Elastic IP Allocation ID</strong>: Enter an existing Elastic IP or select <strong>Allocate Elastic IP address</strong> to allocate a new IP to your NAT gateway.</li><li>Add tags if needed.</li><li>Select <strong>Create NAT Gateway</strong>.</li></ol></li></ol><p>Create a second NAT gateway but this time place it in the second public subnet, <code>gitlab-public-10.0.2.0</code>.</p><h3 id=route-tables>Route Tables</h3><h4 id=public-route-table>Public Route Table</h4><p>We must create a route table for our public subnets to reach the internet via the internet gateway we created in the previous step.</p><p>On the VPC dashboard:</p><ol><li>Select <strong>Route Tables</strong> from the left menu.</li><li>Select <strong>Create Route Table</strong>.</li><li>At the &ldquo;Name tag&rdquo; enter <code>gitlab-public</code> and choose <code>gitlab-vpc</code> under &ldquo;VPC&rdquo;.</li><li>Select <strong>Create</strong>.</li></ol><p>We now must add our internet gateway as a new target and have it receive traffic from any destination.</p><ol><li>Select <strong>Route Tables</strong> from the left menu and select the <code>gitlab-public</code> route to show the options at the bottom.</li><li>Select the <strong>Routes</strong> tab, select <strong>Edit routes > Add route</strong> and set <code>0.0.0.0/0</code> as the destination. In the target column, select the <strong>Internet Gateway</strong> and select the <code>gitlab-gateway</code> we created previously. Select <strong>Save changes</strong> when done.</li></ol><p>Next, we must associate the <strong>public</strong> subnets to the route table:</p><ol><li>Select the <strong>Subnet Associations</strong> tab and select <strong>Edit subnet associations</strong>.</li><li>Check only the public subnets and select <strong>Save associations</strong>.</li></ol><h4 id=private-route-tables>Private Route Tables</h4><p>We also must create two private route tables so that instances in each private subnet can reach the internet via the NAT gateway in the corresponding public subnet in the same availability zone.</p><ol><li>Follow the same steps as above to create two private route tables. Name them <code>gitlab-private-a</code> and <code>gitlab-private-b</code>.</li><li>Next, add a new route to each of the private route tables where the destination is <code>0.0.0.0/0</code> and the target is one of the NAT gateways we created earlier.<ol><li>Add the NAT gateway we created in <code>gitlab-public-10.0.0.0</code> as the target for the new route in the <code>gitlab-private-a</code> route table.</li><li>Similarly, add the NAT gateway in <code>gitlab-public-10.0.2.0</code> as the target for the new route in the <code>gitlab-private-b</code>.</li></ol></li><li>Lastly, associate each private subnet with a private route table.<ol><li>Associate <code>gitlab-private-10.0.1.0</code> with <code>gitlab-private-a</code>.</li><li>Associate <code>gitlab-private-10.0.3.0</code> with <code>gitlab-private-b</code>.</li></ol></li></ol><h2 id=load-balancer>Load Balancer</h2><p>We create a load balancer to evenly distribute inbound traffic on ports <code>80</code> and <code>443</code> across our GitLab application servers. Based on the <a href=/install/aws/#create-an-auto-scaling-group>scaling policies</a> we create later, instances are added to or removed from our load balancer as needed. Additionally, the load balancer performs health checks on our instances. While there are <a href=/administration/load_balancer/#ssl>different ways</a> to handle SSL/TLS in our environment, for this POC we terminate SSL in the load balancer without backend SSL.</p><p>On the EC2 dashboard, look for <strong>Load Balancers</strong> in the left navigation bar:</p><ol><li><p>Select <strong>Create Load Balancer</strong>.</p></li><li><p>Choose the <strong>Network Load Balancer</strong> and select <strong>Create</strong>.</p></li><li><p>Set the Load Balancer name to <code>gitlab-loadbalancer</code>. Set the following additional options:</p><ul><li>Scheme: Select <strong>Internet-facing</strong></li><li>IP address type: Select <strong>IPv4</strong></li><li>VPC: Select the <code>gitlab-vpc</code> from the dropdown list.</li><li>Mapping: Select both public subnets from the list so that the load balancer can route traffic to both availability zones.</li></ul></li><li><p>We add a security group for our load balancer to act as a firewall to control what traffic is allowed through. Under the Security Group section, select the <strong>create a new security group</strong>, give it a name (we use <code>gitlab-loadbalancer-sec-group</code>) and description, and allow both HTTP and HTTPS traffic from anywhere (<code>0.0.0.0/0, ::/0</code>). Also allow SSH traffic, select a custom source, and add a single trusted IP address, or an IP address range in CIDR notation. This allows users to perform Git actions over SSH.</p></li><li><p>In the <strong>Listeners and routing</strong> section, set up listeners for port <code>22</code>, <code>80</code>, and <code>443</code> with the following target groups in mind.</p><table><thead><tr><th>Protocol</th><th>Port</th><th>Target group</th></tr></thead><tbody><tr><td>TCP</td><td>22</td><td><code>gitlab-loadbalancer-ssh-target</code></td></tr><tr><td>TCP</td><td>80</td><td><code>gitlab-loadbalancer-http-target</code></td></tr><tr><td>TLS</td><td>443</td><td><code>gitlab-loadbalancer-http-target</code></td></tr></tbody></table><ol><li>For the TLS listener on port <code>443</code>, under <strong>Security Policy</strong> settings:<ol><li><strong>Policy name:</strong> Pick a predefined security policy from the dropdown list. You can see a breakdown of <a href=https://docs.aws.amazon.com/elasticloadbalancing/latest/network/create-tls-listener.html#describe-ssl-policies>Predefined SSL Security Policies for Network Load Balancers</a> in the AWS documentation. Check the GitLab codebase for a list of <a href=https://gitlab.com/gitlab-org/gitlab/-/blob/9ee7ad433269b37251e0dd5b5e00a0f00d8126b4/lib/support/nginx/gitlab-ssl#L97-99>supported SSL ciphers and protocols</a>.</li><li><strong>Default SSL/TLS server certificate:</strong> Select an SSL/TLS certificate from ACM or upload a certificate to IAM.</li></ol></li></ol></li><li><p>For each listener we created, we need to create a target group and assign them based on the table earlier. Note that we haven&rsquo;t created any EC2 instances yet so you don&rsquo;t need to register targets. The EC2 instances are created and assigned as part of the <a href=/install/aws/#create-an-auto-scaling-group>auto scaling group setup</a> later on.</p><ol><li>Select <code>Create target group</code>.on. Select <strong>Instances</strong> as the target type.</li><li>Select an appropriate <code>Target group name</code> for each listener:<ul><li><code>gitlab-loadbalancer-http-target</code> - TCP Protocol for port 80</li><li><code>gitlab-loadbalancer-ssh-target</code> - TCP Protocol for port 22</li></ul></li><li>Select <strong>IPv4</strong> as the IP address type.</li><li>Select <code>gitlab-vpc</code> from the VPC dropdown list.</li><li>For <code>gitlab-loadbalancer-http-target</code> Health checks, you should <a href=/administration/load_balancer/#readiness-check>use the Readiness check endpoint</a>. You must add <a href=https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-security-groups.html>the VPC IP Address Range (CIDR)</a> to the <a href=/administration/monitoring/ip_allowlist/>IP allowlist</a> for the <a href=/administration/monitoring/health_check/>Health check endpoints</a></li><li>For <code>gitlab-loadbalancer-ssh-target</code> Health checks, select <strong>TCP</strong>.<ul><li>Assign <code>gitlab-loadbalancer-http-target</code> to both port 80 and 443 listener.</li><li>Assign <code>gitlab-loadbalancer-ssh-target</code> to port 22 listener.</li></ul></li><li>Some attributes can only be configured after the target groups have already been created. Here are a couple of features you might configure based on your requirements.<ul><li><p><a href=https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#client-ip-preservation>Client IP preservation</a> is enabled for the target groups by default. This allows the IP of the client connected in the Load Balancer to be preserved in the GitLab application. You can make enable/disable this based on your requirements.</p></li><li><p><a href=https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol>Proxy Protocol</a> is disabled for the target groups by default. This allows the Load Balancer to send additional information in the proxy protocol headers. If you want to enable this, make sure that other environment components like internal load balancers, NGINX, etc. are configured as well. For this POC we only need to enable it in the <a href=/install/aws/#proxy-protocol>GitLab node later</a>.</p></li></ul></li></ol></li><li><p>Select <strong>Create load balancer</strong>.</p></li></ol><p>After the Load Balancer is up and running, you can revisit your Security Groups to refine the access only through the NLB and any other requirements you might have.</p><h3 id=configure-dns-for-load-balancer>Configure DNS for Load Balancer</h3><p>On the Route 53 dashboard, select <strong>Hosted zones</strong> in the left navigation bar:</p><ol><li>Select an existing hosted zone or, if you do not already have one for your domain, select <strong>Create Hosted Zone</strong>, enter your domain name, and select <strong>Create</strong>.</li><li>Select <strong>Create record</strong> and provide the following values:<ol><li><strong>Name:</strong> Use the domain name (the default value) or enter a subdomain.</li><li><strong>Type:</strong> Select <strong>A - IPv4 address</strong>.</li><li><strong>Alias:</strong> Defaults to <strong>disabled</strong>. Enable this option.</li><li><strong>Route traffic to:</strong> Select <strong>Alias to Network Load Balancer</strong>.</li><li><strong>Region:</strong> Select the region where the Network Load Balancer resides.</li><li><strong>Choose network load balancer:</strong> Select the Network Load Balancer we created earlier.</li><li><strong>Routing Policy:</strong> We use <strong>Simple</strong> but you can choose a different policy based on your use case.</li><li><strong>Evaluate Target Health:</strong> We set this to <strong>No</strong> but you can choose to have the load balancer route traffic based on target health.</li><li>Select <strong>Create</strong>.</li></ol></li><li>If you registered your domain through Route 53, you&rsquo;re done. If you used a different domain registrar, you must update your DNS records with your domain registrar. You must:<ol><li>Select <strong>Hosted zones</strong> and select the domain you added above.</li><li>You see a list of <code>NS</code> records. From your domain registrar&rsquo;s administrator panel, add each of these as <code>NS</code> records to your domain&rsquo;s DNS records. These steps may vary between domain registrars. If you&rsquo;re stuck, Google <strong>&ldquo;name of your registrar&rdquo; add DNS records</strong> and you should find a help article specific to your domain registrar.</li></ol></li></ol><p>The steps for doing this vary depending on which registrar you use and is beyond the scope of this guide.</p><h2 id=postgresql-with-rds>PostgreSQL with RDS</h2><p>For our database server we use Amazon RDS for PostgreSQL which offers Multi AZ for redundancy (<a href=https://gitlab.com/gitlab-partners-public/aws/aws-known-issues/-/issues/10>Aurora is <strong>not</strong> supported</a>). First we create a security group and subnet group, then we create the actual RDS instance.</p><h3 id=rds-security-group>RDS Security Group</h3><p>We need a security group for our database that allows inbound traffic from the instances we deploy in our <code>gitlab-loadbalancer-sec-group</code> later on:</p><ol><li>From the EC2 dashboard, select <strong>Security Groups</strong> from the left menu bar.</li><li>Select <strong>Create security group</strong>.</li><li>Give it a name (we use <code>gitlab-rds-sec-group</code>), a description, and select the <code>gitlab-vpc</code> from the <strong>VPC</strong> dropdown list.</li><li>In the <strong>Inbound rules</strong> section, select <strong>Add rule</strong> and set the following:<ol><li><strong>Type:</strong> search for and select the <strong>PostgreSQL</strong> rule.</li><li><strong>Source type:</strong> set as &ldquo;Custom&rdquo;.</li><li><strong>Source:</strong> select the <code>gitlab-loadbalancer-sec-group</code> we created earlier.</li></ol></li><li>When done, select <strong>Create security group</strong>.</li></ol><h3 id=rds-subnet-group>RDS Subnet Group</h3><ol><li>Go to the RDS dashboard and select <strong>Subnet Groups</strong> from the left menu.</li><li>Select <strong>Create DB Subnet Group</strong>.</li><li>Under <strong>Subnet group details</strong>, enter a name (we use <code>gitlab-rds-group</code>), a description, and choose the <code>gitlab-vpc</code> from the VPC dropdown list.</li><li>From the <strong>Availability Zones</strong> dropdown list, select the Availability Zones that include the subnets you&rsquo;ve configured. In our case, we add <code>eu-west-2a</code> and <code>eu-west-2b</code>.</li><li>From the <strong>Subnets</strong> dropdown list, select the two private subnets (<code>10.0.1.0/24</code> and <code>10.0.3.0/24</code>) as we defined them in the <a href=/install/aws/#subnets>subnets section</a>.</li><li>Select <strong>Create</strong> when ready.</li></ol><h3 id=create-the-database>Create the database</h3><div class="alert alert-type-warning"><p>Avoid using burstable instances (t class instances) for the database as this could lead to performance issues due to CPU credits running out during sustained periods of high load.</p></div><p>Now, it&rsquo;s time to create the database:</p><ol><li><p>Go to the RDS dashboard, select <strong>Databases</strong> from the left menu, and select <strong>Create database</strong>.</p></li><li><p>Select <strong>Standard Create</strong> for the database creation method.</p></li><li><p>Select <strong>PostgreSQL</strong> as the database engine and select the minimum PostgreSQL version as defined for your GitLab version in our <a href=/install/requirements/#postgresql>database requirements</a>.</p></li><li><p>Because this is a production server, let&rsquo;s choose <strong>Production</strong> from the <strong>Templates</strong> section.</p></li><li><p>Under <strong>Availability & durability</strong>, select <strong>Multi-AZ DB instance</strong> to have a standby RDS instance provisioned in a different <a href=https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html>Availability Zone</a>.</p></li><li><p>Under <strong>Settings</strong>, use:</p><ul><li><code>gitlab-db-ha</code> for the DB instance identifier.</li><li><code>gitlab</code> for a master username.</li><li>A very secure password for the master password.</li></ul><p>Make a note of these as we need them later.</p></li><li><p>For the DB instance size, select <strong>Standard classes</strong> and select an instance size that meets your requirements from the dropdown list. We use a <code>db.m5.large</code> instance.</p></li><li><p>Under <strong>Storage</strong>, configure the following:</p><ol><li>Select <strong>Provisioned IOPS (SSD)</strong> from the storage type dropdown list. Provisioned IOPS (SSD) storage is best suited for this use (though you can choose General Purpose (SSD) to reduce the costs). Read more about it at <a href=https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html>Storage for Amazon RDS</a>.</li><li>Allocate storage and set provisioned IOPS. We use the minimum values, <code>100</code> and <code>1000</code>, respectively.</li><li>Enable storage autoscaling (optional) and set a maximum storage threshold.</li></ol></li><li><p>Under <strong>Connectivity</strong>, configure the following:</p><ol><li>Under the <strong>Virtual Private Cloud (VPC)</strong> dropdown list select the VPC we created earlier (<code>gitlab-vpc</code>).</li><li>Under the <strong>DB subnet group</strong> select the subnet group (<code>gitlab-rds-group</code>) we created earlier.</li><li>Set public access to <strong>No</strong>.</li><li>Under <strong>VPC security group</strong>, select <strong>Choose existing</strong> and select the <code>gitlab-rds-sec-group</code> we create above from the dropdown list.</li><li>Under <strong>Additional configuration</strong> leave the database port as the default <code>5432</code>.</li></ol></li><li><p>For <strong>Database authentication</strong>, select <strong>Password authentication</strong>.</p></li><li><p>Expand the <strong>Additional configuration</strong> section and complete the following:</p><ol><li>The initial database name. We use <code>gitlabhq_production</code>.</li><li>Configure your preferred backup settings.</li><li>The only other change we make here is to disable auto minor version updates under <strong>Maintenance</strong>.</li><li>Leave all the other settings as is or tweak according to your needs.</li><li>If you&rsquo;re happy, select <strong>Create database</strong>.</li></ol></li></ol><p>Now that the database is created, let&rsquo;s move on to setting up Redis with ElastiCache.</p><h2 id=redis-with-elasticache>Redis with ElastiCache</h2><p>ElastiCache is an in-memory hosted caching solution. Redis maintains its own persistence and is used to store session data, temporary cache information, and background job queues for the GitLab application.</p><h3 id=create-a-redis-security-group>Create a Redis Security Group</h3><ol><li>Go to the EC2 dashboard.</li><li>Select <strong>Security Groups</strong> from the left menu.</li><li>Select <strong>Create security group</strong> and fill in the details. Give it a name (we use <code>gitlab-redis-sec-group</code>), add a description, and choose the VPC we created earlier (<code>gitlab-vpc</code>).</li><li>In the <strong>Inbound rules</strong> section, select <strong>Add rule</strong> and add a <strong>Custom TCP</strong> rule, set port <code>6379</code>, and set the &ldquo;Custom&rdquo; source as the <code>gitlab-loadbalancer-sec-group</code> we created earlier.</li><li>When done, select <strong>Create security group</strong>.</li></ol><h3 id=redis-subnet-group>Redis Subnet Group</h3><ol><li><p>Go to the ElastiCache dashboard from your AWS console.</p></li><li><p>Go to <strong>Subnet Groups</strong> in the left menu, and create a new subnet group (we name ours <code>gitlab-redis-group</code>). Select the VPC we created earlier (<code>gitlab-vpc</code>) and ensure the selected subnets table only contains the <a href=/install/aws/#subnets>private subnets</a>.</p></li><li><p>Select <strong>Create</strong> when ready.</p><p><a href=/install/aws/img/ec_subnet_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Create a subnet group" src=/install/aws/img/ec_subnet_v17_0.png></a></p></li></ol><h3 id=create-the-redis-cluster>Create the Redis Cluster</h3><ol><li>Go back to the ElastiCache dashboard.</li><li>Select <strong>Redis caches</strong> on the left menu and select <strong>Create Redis cache</strong> to create a new Redis cluster.</li><li>Under <strong>Deployment option</strong> select <strong>Design your own cache</strong>.</li><li>Under <strong>Creation method</strong> select <strong>Cluster cache</strong>.</li><li>Under <strong>Cluster mode</strong> select <strong>Disabled</strong> as it is <a href=/administration/redis/replication_and_failover_external/#requirements>not supported</a>. Even without cluster mode on, you still get the chance to deploy Redis in multiple availability zones.</li><li>Under <strong>Cluster info</strong> give the cluster a name (<code>gitlab-redis</code>) and a description.</li><li>Under <strong>Location</strong> select <strong>AWS Cloud</strong> and enable <strong>Multi-AZ</strong> option.</li><li>In the Cluster settings section:<ol><li>For the Engine version, select the Redis version as defined for your GitLab version in our <a href=/install/requirements/#redis>Redis requirements</a>.</li><li>Leave the port as <code>6379</code> because this is what we used in our Redis security group above.</li><li>Select the node type (at least <code>cache.t3.medium</code>, but adjust to your needs) and the number of replicas.</li></ol></li><li>In the Connectivity settings section:<ol><li><strong>Network type:</strong> IPv4</li><li><strong>Subnet groups:</strong> Select <strong>Choose existing subnet group</strong> and choose the <code>gitlab-redis-group</code> we had previously created.</li></ol></li><li>In the Availability Zone placements section:<ol><li><p>Manually select the preferred availability zones, and under &ldquo;Replica 2&rdquo; choose a different zone than the other two.</p><p><a href=/install/aws/img/ec_az_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Redis availability zones" src=/install/aws/img/ec_az_v17_0.png></a></p></li></ol></li><li>Select <strong>Next</strong>.</li><li>In the security settings, edit the security groups and choose the <code>gitlab-redis-sec-group</code> we had previously created. Select <strong>Next</strong>.</li><li>Leave the rest of the settings to their default values or edit to your liking.</li><li>When done, select <strong>Create</strong>.</li></ol><h2 id=setting-up-bastion-hosts>Setting up Bastion Hosts</h2><p>Because our GitLab instances are in private subnets, we need a way to connect to these instances with SSH for actions that include making configuration changes and performing upgrades. One way of doing this is by using a <a href=https://en.wikipedia.org/wiki/Bastion_host>bastion host</a>, sometimes also referred to as a jump box.</p><div class="alert alert-type-note"><p>If you do not want to maintain bastion hosts, you can set up <a href=https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html>AWS Systems Manager Session Manager</a> for access to instances. This is beyond the scope of this document.</p></div><h3 id=create-bastion-host-a>Create Bastion Host A</h3><ol><li>Go to the EC2 Dashboard and select <strong>Launch instance</strong>.</li><li>In the <strong>Name and tags</strong> section, set the <strong>Name</strong> to <code>Bastion Host A</code>.</li><li>Select the latest <strong>Ubuntu Server LTS (HVM)</strong> AMI. Check the GitLab documentation for the <a href=/administration/package_information/supported_os/>latest supported OS version</a>.</li><li>Choose an instance type. We use a <code>t2.micro</code> as we only use the bastion host to SSH into our other instances.</li><li>In the <strong>Key pair</strong> section, select <strong>Create new key pair</strong>.<ol><li>Give the key pair a name (we use <code>bastion-host-a</code>) and save the <code>bastion-host-a.pem</code> file for later use.</li></ol></li><li>Edit the Network settings section:<ol><li>Under <strong>VPC</strong>, select the <code>gitlab-vpc</code> from the dropdown list.</li><li>Under <strong>Subnet</strong>, select the public subnet we created earlier (<code>gitlab-public-10.0.0.0</code>).</li><li>Check that under <strong>Auto-assign Public IP</strong> you have <strong>Disabled</strong> selected. An <a href=https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html>Elastic IP address</a> is assigned later to the host in the <a href=/install/aws/#assign-elastic-ip-to-the-bastion-host-a>next section</a>.</li><li>Under <strong>Firewall</strong> select <strong>Create security group</strong>, enter a <strong>Security group name</strong> (we use <code>bastion-sec-group</code>), and add a description.</li><li>We enable SSH access from anywhere (<code>0.0.0.0/0</code>). If you want stricter security, specify a single IP address or an IP address range in CIDR notation.</li></ol></li><li>For storage, we leave everything as default and only add an 8 GB root volume. We do not store anything on this instance.</li><li>Review all your settings and, if you&rsquo;re happy, select <strong>Launch Instance</strong>.</li></ol><h4 id=assign-elastic-ip-to-the-bastion-host-a>Assign Elastic IP to the Bastion Host A</h4><ol><li>Go to the EC2 Dashboard and select <strong>Network & Security</strong>.</li><li>Select <strong>Elastic IPs</strong> and set the <code>Network border group</code> to <code>us-west-2</code>.</li><li>Select <strong>Allocate</strong>.</li><li>Select the Elastic IP address that was created.</li><li>Select <strong>Actions</strong> and choose <strong>Associate Elastic IP address</strong>.</li><li>Under the <strong>Resource Type</strong> select <strong>Instance</strong> and choose the <code>Bastion Host A</code> host under the <strong>Instance</strong> dropdown list.</li><li>Select <strong>Associate</strong>.</li></ol><h4 id=confirm-that-you-can-ssh-into-the-instance>Confirm that you can SSH into the instance</h4><ol><li>On the EC2 Dashboard, select <strong>Instances</strong> in the left menu.</li><li>Select <strong>Bastion Host A</strong> from your list of instances.</li><li>Select <strong>Connect</strong> and follow the connection instructions.</li><li>If you are able to connect successfully, let&rsquo;s move on to setting up our second bastion host for redundancy.</li></ol><h3 id=create-bastion-host-b>Create Bastion Host B</h3><ol><li>Create an EC2 instance following the same steps as above with the following changes:<ol><li>For the <strong>Subnet</strong>, select the second public subnet we created earlier (<code>gitlab-public-10.0.2.0</code>).</li><li>Under the <strong>Add Tags</strong> section, we set <code>Key: Name</code> and <code>Value: Bastion Host B</code> so that we can easily identify our two instances.</li><li>For the security group, select the existing <code>bastion-sec-group</code> we created above.</li></ol></li></ol><h3 id=use-ssh-agent-forwarding>Use SSH Agent Forwarding</h3><p>EC2 instances running Linux use private key files for SSH authentication. You connect to your bastion host using an SSH client and the private key file stored on your client. Because the private key file is not present on the bastion host, you are not able to connect to your instances in private subnets.</p><p>Storing private key files on your bastion host is a bad idea. To get around this, use SSH agent forwarding on your client.</p><p>For example, the command-line <code>ssh</code> client uses agent forwarding with its <code>-A</code> switch, like this:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>ssh –A user@&lt;bastion-public-IP-address&gt;</span></span></code></pre></div></div><p>See <a href=https://aws.amazon.com/blogs/security/securely-connect-to-linux-instances-running-in-a-private-amazon-vpc/>Securely Connect to Linux Instances Running in a Private Amazon VPC</a> for a step-by-step guide on how to use SSH agent forwarding for other clients.</p><h2 id=install-gitlab-and-create-custom-ami>Install GitLab and create custom AMI</h2><p>We need a preconfigured, custom GitLab AMI to use in our launch configuration later. As a starting point, we use the official GitLab AMI to create a GitLab instance. Then, we add our custom configuration for PostgreSQL, Redis, and Gitaly. If you prefer, instead of using the official GitLab AMI, you can also spin up an EC2 instance of your choosing and <a href=https://about.gitlab.com/install/>manually install GitLab</a>.</p><h3 id=install-gitlab>Install GitLab</h3><p>From the EC2 dashboard:</p><ol><li>Use the section below titled &ldquo;<a href=/install/aws/#find-official-gitlab-created-ami-ids-on-aws>Find official GitLab-created AMI IDs on AWS</a>&rdquo; to find the correct AMI and select <strong>Launch</strong>.</li><li>In the <strong>Name and tags</strong> section, set the <strong>Name</strong> to <code>GitLab</code>.</li><li>In the <strong>Instance type</strong> dropdown list, select an instance type based on your workload. Consult the <a href=/install/requirements/>hardware requirements</a> to choose one that fits your needs (at least <code>c5.2xlarge</code>, which is sufficient to accommodate 100 users).</li><li>In the <strong>Key pair</strong> section, select <strong>Create new key pair</strong>.<ol><li>Give the key pair a name (we use <code>gitlab</code>) and save the <code>gitlab.pem</code> file for later use.</li></ol></li><li>In the <strong>Network settings</strong> section:<ol><li><strong>VPC:</strong> Select <code>gitlab-vpc</code>, the VPC we created earlier.</li><li><strong>Subnet:</strong> Select <code>gitlab-private-10.0.1.0</code> from the list of subnets we created earlier.</li><li><strong>Auto-assign Public IP:</strong> Select <code>Disable</code>.</li><li><strong>Firewall:</strong> Chose <strong>Select existing security group</strong> and select the <code>gitlab-loadbalancer-sec-group</code> we created earlier.</li></ol></li><li>For storage, the root volume is 8 GiB by default and should be enough given that we do not store any data there.</li><li>Review all your settings and, if you&rsquo;re happy, select <strong>Launch Instance</strong>.</li></ol><h3 id=add-custom-configuration>Add custom configuration</h3><p>Connect to your GitLab instance via <strong>Bastion Host A</strong> using <a href=/install/aws/#use-ssh-agent-forwarding>SSH Agent Forwarding</a>. Once connected, add the following custom configuration:</p><h4 id=disable-lets-encrypt>Disable Let&rsquo;s Encrypt</h4><p>Because we&rsquo;re adding our SSL certificate at the load balancer, we do not need the GitLab built-in support for Let&rsquo;s Encrypt. Let&rsquo;s Encrypt <a href=https://docs.gitlab.com/omnibus/settings/ssl/#enable-the-lets-encrypt-integration>is enabled by default</a> when using an <code>https</code> domain, so we must explicitly disable it:</p><ol><li><p>Open <code>/etc/gitlab/gitlab.rb</code> and disable it:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=ruby class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-ruby data-lang=ruby><span style=display:flex><span>letsencrypt<span style=color:#666>[</span><span style=color:#ba2121>&#39;enable&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:green>false</span></span></span></code></pre></div></div></li><li><p>Save the file and reconfigure for the changes to take effect:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo gitlab-ctl reconfigure</span></span></code></pre></div></div></li></ol><h4 id=install-the-required-extensions-for-postgresql>Install the required extensions for PostgreSQL</h4><p>From your GitLab instance, connect to the RDS instance to verify access and to install the required <code>pg_trgm</code> and <code>btree_gist</code> extensions.</p><p>To find the host or endpoint, go to <strong>Amazon RDS > Databases</strong> and select the database you created earlier. Look for the endpoint under the <strong>Connectivity & security</strong> tab.</p><p>Do not to include the colon and port number:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo /opt/gitlab/embedded/bin/psql -U gitlab -h &lt;rds-endpoint&gt; -d gitlabhq_production</span></span></code></pre></div></div><p>At the <code>psql</code> prompt create the extension and then quit the session:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>psql <span style=color:#666>(</span>10.9<span style=color:#666>)</span> </span></span><span style=display:flex><span>Type <span style=color:#ba2121>&#34;help&#34;</span> <span style=color:green;font-weight:700>for</span> help. </span></span><span style=display:flex><span> </span></span><span style=display:flex><span><span style=color:#19177c>gitlab</span><span style=color:#666>=</span><span style=color:#408080;font-style:italic># CREATE EXTENSION pg_trgm;</span> </span></span><span style=display:flex><span><span style=color:#19177c>gitlab</span><span style=color:#666>=</span><span style=color:#408080;font-style:italic># CREATE EXTENSION btree_gist;</span> </span></span><span style=display:flex><span><span style=color:#19177c>gitlab</span><span style=color:#666>=</span><span style=color:#408080;font-style:italic># \q</span></span></span></code></pre></div></div><h4 id=configure-gitlab-to-connect-to-postgresql-and-redis>Configure GitLab to connect to PostgreSQL and Redis</h4><ol><li><p>Edit <code>/etc/gitlab/gitlab.rb</code>, find the <code>external_url 'http://&lt;domain>'</code> option and change it to the <code>https</code> domain you are using.</p></li><li><p>Look for the GitLab database settings and uncomment as necessary. In our current case we specify the database adapter, encoding, host, name, username, and password:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=ruby class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-ruby data-lang=ruby><span style=display:flex><span><span style=color:#408080;font-style:italic># Disable the built-in Postgres</span> </span></span><span style=display:flex><span> postgresql<span style=color:#666>[</span><span style=color:#ba2121>&#39;enable&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:green>false</span> </span></span><span style=display:flex><span> </span></span><span style=display:flex><span><span style=color:#408080;font-style:italic># Fill in the connection details</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;db_adapter&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#ba2121>&#34;postgresql&#34;</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;db_encoding&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#ba2121>&#34;unicode&#34;</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;db_database&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#ba2121>&#34;gitlabhq_production&#34;</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;db_username&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#ba2121>&#34;gitlab&#34;</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;db_password&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#ba2121>&#34;mypassword&#34;</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;db_host&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#ba2121>&#34;&lt;rds-endpoint&gt;&#34;</span></span></span></code></pre></div></div></li><li><p>Next, we must configure the Redis section by adding the host and uncommenting the port:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=ruby class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-ruby data-lang=ruby><span style=display:flex><span><span style=color:#408080;font-style:italic># Disable the built-in Redis</span> </span></span><span style=display:flex><span>redis<span style=color:#666>[</span><span style=color:#ba2121>&#39;enable&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:green>false</span> </span></span><span style=display:flex><span> </span></span><span style=display:flex><span><span style=color:#408080;font-style:italic># Fill in the connection details</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;redis_host&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#ba2121>&#34;&lt;redis-endpoint&gt;&#34;</span> </span></span><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;redis_port&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#666>6379</span></span></span></code></pre></div></div></li><li><p>Finally, reconfigure GitLab for the changes to take effect:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo gitlab-ctl reconfigure</span></span></code></pre></div></div></li><li><p>You can also run a check and a service status to make sure everything has been setup correctly:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo gitlab-rake gitlab:check </span></span><span style=display:flex><span>sudo gitlab-ctl status</span></span></code></pre></div></div></li></ol><h4 id=set-up-gitaly>Set up Gitaly</h4><div class="alert alert-type-warning"><p>In this architecture, having a single Gitaly server creates a single point of failure. Use <a href=/administration/gitaly/praefect/>Gitaly Cluster</a> to remove this limitation.</p></div><p>Gitaly is a service that provides high-level RPC access to Git repositories. It should be enabled and configured on a separate EC2 instance in one of the <a href=/install/aws/#subnets>private subnets</a> we configured previously.</p><p>Let&rsquo;s create an EC2 instance where we install Gitaly:</p><ol><li>From the EC2 dashboard, select <strong>Launch instance</strong>.</li><li>In the <strong>Name and tags</strong> section, set the <strong>Name</strong> to <code>Gitaly</code>.</li><li>Choose an AMI. In this example, we select the latest <strong>Ubuntu Server LTS (HVM), SSD Volume Type</strong>. Check the GitLab documentation for the <a href=/administration/package_information/supported_os/>latest supported OS version</a>.</li><li>Choose an instance type. We pick a <code>m5.xlarge</code>.</li><li>In the <strong>Key pair</strong> section, select <strong>Create new key pair</strong>.<ol><li>Give the key pair a name (we use <code>gitaly</code>) and save the <code>gitaly.pem</code> file for later use.</li></ol></li><li>In the Network settings section:<ol><li>Under <strong>VPC</strong>, select the <code>gitlab-vpc</code> from the dropdown list.</li><li>Under <strong>Subnet</strong>, select the private subnet we created earlier (<code>gitlab-private-10.0.1.0</code>).</li><li>Check that under <strong>Auto-assign Public IP</strong> you have <strong>Disable</strong> selected.</li><li>Under <strong>Firewall</strong> select <strong>Create security group</strong>, enter a <strong>Security group name</strong> (we use <code>gitlab-gitaly-sec-group</code>), and add a description.<ol><li>Create a <strong>Custom TCP</strong> rule and add port <code>8075</code> to the <strong>Port Range</strong>. For the <strong>Source</strong>, select the <code>gitlab-loadbalancer-sec-group</code>.</li><li>Also add an inbound rule for SSH from the <code>bastion-sec-group</code> so that we can connect using <a href=/install/aws/#use-ssh-agent-forwarding>SSH Agent Forwarding</a> from the Bastion hosts.</li></ol></li></ol></li><li>Increase the Root volume size to <code>20 GiB</code> and change the <strong>Volume Type</strong> to <code>Provisioned IOPS SSD (io1)</code>. (The volume size is an arbitrary value. Create a volume big enough for your repository storage requirements.)<ol><li>For <strong>IOPS</strong> set <code>1000</code> (20 GiB x 50 IOPS). You can provision up to 50 IOPS per GiB. If you select a larger volume, increase the IOPS accordingly. Workloads where many small files are written in a serialized manner, like <code>git</code>, requires performant storage, hence the choice of <code>Provisioned IOPS SSD (io1)</code>.</li></ol></li><li>Review all your settings and, if you&rsquo;re happy, select <strong>Launch Instance</strong>.</li></ol><div class="alert alert-type-note"><p>Instead of storing configuration <em>and</em> repository data on the root volume, you can also choose to add an additional EBS volume for repository storage. Follow the same guidance as above. See the <a href=https://aws.amazon.com/ebs/pricing/>Amazon EBS pricing</a>. We do not recommend using EFS as it may negatively impact the performance of GitLab. You can review the <a href=/administration/nfs/#avoid-using-cloud-based-file-systems>relevant documentation</a> for more details.</p></div><p>Now that we have our EC2 instance ready, follow the <a href=/administration/gitaly/configure_gitaly/#run-gitaly-on-its-own-server>documentation to install GitLab and set up Gitaly on its own server</a>. Perform the client setup steps from that document on the <a href=/install/aws/#install-gitlab>GitLab instance we created</a> above.</p><h4 id=add-support-for-proxied-ssl>Add Support for Proxied SSL</h4><p>As we are terminating SSL at our <a href=/install/aws/#load-balancer>load balancer</a>, follow the steps at <a href=https://docs.gitlab.com/omnibus/settings/ssl/#configure-a-reverse-proxy-or-load-balancer-ssl-termination>Supporting proxied SSL</a> to configure this in <code>/etc/gitlab/gitlab.rb</code>.</p><p>Remember to run <code>sudo gitlab-ctl reconfigure</code> after saving the changes to the <code>gitlab.rb</code> file.</p><h4 id=fast-lookup-of-authorized-ssh-keys>Fast lookup of authorized SSH keys</h4><p>The public SSH keys for users allowed to access GitLab are stored in <code>/var/opt/gitlab/.ssh/authorized_keys</code>. Typically we&rsquo;d use shared storage so that all the instances are able to access this file when a user performs a Git action over SSH. Because we do not have shared storage in our setup, we update our configuration to authorize SSH users via indexed lookup in the GitLab database.</p><p>Follow the instructions at <a href=/administration/operations/fast_ssh_key_lookup/#set-up-fast-lookup>Set up fast SSH key lookup</a> to switch from using the <code>authorized_keys</code> file to the database.</p><p>If you do not configure fast lookup, Git actions over SSH results in the following error:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>Permission denied <span style=color:#666>(</span>publickey<span style=color:#666>)</span>. </span></span><span style=display:flex><span>fatal: Could not <span style=color:green>read</span> from remote repository. </span></span><span style=display:flex><span> </span></span><span style=display:flex><span>Please make sure you have the correct access rights </span></span><span style=display:flex><span>and the repository exists.</span></span></code></pre></div></div><h4 id=configure-host-keys>Configure host keys</h4><p>Ordinarily we would manually copy the contents (primary and public keys) of <code>/etc/ssh/</code> on the primary application server to <code>/etc/ssh</code> on all secondary servers. This prevents false man-in-the-middle-attack alerts when accessing servers in your cluster behind a load balancer.</p><p>We automate this by creating static host keys as part of our custom AMI. As these host keys are also rotated every time an EC2 instance boots up, &ldquo;hard coding&rdquo; them into our custom AMI serves as a workaround.</p><p>On your GitLab instance run the following:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo mkdir /etc/ssh_static </span></span><span style=display:flex><span>sudo cp -R /etc/ssh/* /etc/ssh_static</span></span></code></pre></div></div><p>In <code>/etc/ssh/sshd_config</code> update the following:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span><span style=color:#408080;font-style:italic># HostKeys for protocol version 2</span> </span></span><span style=display:flex><span>HostKey /etc/ssh_static/ssh_host_rsa_key </span></span><span style=display:flex><span>HostKey /etc/ssh_static/ssh_host_dsa_key </span></span><span style=display:flex><span>HostKey /etc/ssh_static/ssh_host_ecdsa_key </span></span><span style=display:flex><span>HostKey /etc/ssh_static/ssh_host_ed25519_key</span></span></code></pre></div></div><h4 id=amazon-s3-object-storage>Amazon S3 object storage</h4><p>Because we&rsquo;re not using NFS for shared storage, we use <a href=https://aws.amazon.com/s3/>Amazon S3</a> buckets to store backups, artifacts, LFS objects, uploads, merge request diffs, container registry images, and more. Our documentation includes <a href=/administration/object_storage/>instructions on how to configure object storage</a> for each of these data types, and other information about using object storage with GitLab.</p><div class="alert alert-type-note"><p>Because we are using the <a href=/install/aws/#create-an-iam-role>AWS IAM profile</a> we created earlier, be sure to omit the AWS access key and secret access key/value pairs when configuring object storage. Instead, use <code>'use_iam_profile' => true</code> in your configuration as shown in the object storage documentation linked above.</p></div><p>Remember to run <code>sudo gitlab-ctl reconfigure</code> after saving the changes to the <code>gitlab.rb</code> file.</p><hr><p>That concludes the configuration changes for our GitLab instance. Next, we create a custom AMI based on this instance to use for our launch configuration and auto scaling group.</p><h3 id=ip-allowlist>IP allowlist</h3><p>We must add <a href=https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-security-groups.html>the VPC IP Address Range (CIDR)</a> of the <code>gitlab-vpc</code> we created earlier to the <a href=/administration/monitoring/ip_allowlist/>IP allowlist</a> for the <a href=/administration/monitoring/health_check/>Health check endpoints</a></p><ol><li><p>Edit <code>/etc/gitlab/gitlab.rb</code>:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=ruby class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-ruby data-lang=ruby><span style=display:flex><span>gitlab_rails<span style=color:#666>[</span><span style=color:#ba2121>&#39;monitoring_whitelist&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#666>[</span><span style=color:#ba2121>&#39;127.0.0.0/8&#39;</span>, <span style=color:#ba2121>&#39;10.0.0.0/16&#39;</span><span style=color:#666>]</span></span></span></code></pre></div></div></li><li><p>Reconfigure GitLab:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo gitlab-ctl reconfigure</span></span></code></pre></div></div></li></ol><h3 id=proxy-protocol>Proxy Protocol</h3><p>If Proxy protocol is enabled in the <a href=/install/aws/#load-balancer>load balancer</a> we created earlier, we must also <a href=https://docs.gitlab.com/omnibus/settings/nginx.html#configuring-the-proxy-protocol>enable</a> this on the <code>gitlab.rb</code> file.</p><ol><li><p>Edit <code>/etc/gitlab/gitlab.rb</code>:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=ruby class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-ruby data-lang=ruby><span style=display:flex><span>nginx<span style=color:#666>[</span><span style=color:#ba2121>&#39;proxy_protocol&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:green>true</span> </span></span><span style=display:flex><span>nginx<span style=color:#666>[</span><span style=color:#ba2121>&#39;real_ip_trusted_addresses&#39;</span><span style=color:#666>]</span> <span style=color:#666>=</span> <span style=color:#666>[</span> <span style=color:#ba2121>&#34;127.0.0.0/8&#34;</span>, <span style=color:#ba2121>&#34;IP_OF_THE_PROXY/32&#34;</span><span style=color:#666>]</span></span></span></code></pre></div></div></li><li><p>Reconfigure GitLab:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo gitlab-ctl reconfigure</span></span></code></pre></div></div></li></ol><h3 id=sign-in-for-the-first-time>Sign in for the first time</h3><p>Using the domain name you used when setting up <a href=/install/aws/#configure-dns-for-load-balancer>DNS for the load balancer</a>, you should now be able to visit GitLab in your browser.</p><p>Depending on how you installed GitLab and if you did not change the password by any other means, the default password is either:</p><ul><li>Your instance ID if you used the official GitLab AMI.</li><li>A randomly generated password stored for 24 hours in <code>/etc/gitlab/initial_root_password</code>.</li></ul><p>To change the default password, sign in as the <code>root</code> user with the default password and <a href=/user/profile/user_passwords/#change-your-password>change it in the user profile</a>.</p><p>When our <a href=/install/aws/#create-an-auto-scaling-group>auto scaling group</a> spins up new instances, we are able to sign in with username <code>root</code> and the newly created password.</p><h3 id=create-custom-ami>Create custom AMI</h3><p>On the EC2 dashboard:</p><ol><li>Select the <code>GitLab</code> instance we <a href=/install/aws/#install-gitlab>created earlier</a>.</li><li>Select <strong>Actions</strong>, scroll down to <strong>Image and templates</strong> and select <strong>Create image</strong>.</li><li>Give your image a name and description (we use <code>GitLab-Source</code> for both).</li><li>Leave everything else as default and select <strong>Create Image</strong></li></ol><p>Now we have a custom AMI that we use to create our launch configuration the next step.</p><h2 id=deploy-gitlab-inside-an-auto-scaling-group>Deploy GitLab inside an auto scaling group</h2><h3 id=create-a-launch-template>Create a launch template</h3><p>From the EC2 dashboard:</p><ol><li>Select <strong>Launch Templates</strong> from the left menu and select <strong>create launch template</strong>.</li><li>Enter a name for your launch template (we use <code>gitlab-launch-template</code>).</li><li>Select <strong>Launch template contents</strong> and select <strong>My AMIs</strong> tab/</li><li>Select <strong>Owned by me</strong> and select the <code>GitLab-Source</code> custom AMI we created above.</li><li>Select an instance type best suited for your needs (at least a <code>c5.2xlarge</code>).</li><li>In the <strong>Key pair</strong> section, select <strong>Create new key pair</strong>.<ol><li>Give the key pair a name (we use <code>gitlab-launch-template</code>) and save the <code>gitlab-launch-template.pem</code> file for later use.</li></ol></li><li>The root volume is 8 GiB by default and should be enough given that we do not store any data there. Select <strong>Configure Security Group</strong>.</li><li>Check <strong>Select and existing security group</strong> and select the <code>gitlab-loadbalancer-sec-group</code> we created earlier.</li><li>In the <strong>Network settings</strong> section:<ol><li><strong>Firewall:</strong> Choose <strong>Select existing security group</strong> and select the <code>gitlab-loadbalancer-sec-group</code> we created earlier.</li></ol></li><li>In the <strong>Advanced details</strong> section:<ol><li><strong>IAM instance profile:</strong> Select the <code>GitLabS3Access</code> role we <a href=/install/aws/#create-an-iam-role>created earlier</a>.</li></ol></li><li>Review all your settings and, if you&rsquo;re happy, select <strong>Create launch template</strong>.</li></ol><h3 id=create-an-auto-scaling-group>Create an auto scaling group</h3><p>From the EC2 dashboard:</p><ol><li><p>Select <strong>Auto scaling groups</strong> from the left menu and select <strong>Create Auto Scaling group</strong>.</p></li><li><p>Enter a <strong>Group name</strong> (we use <code>gitlab-auto-scaling-group</code>).</p></li><li><p>Under <strong>Launch template</strong>, select the launch template we created earlier. Select <strong>Next</strong></p></li><li><p>In the Network settings section:</p><ol><li>Under <strong>VPC</strong>, select the <code>gitlab-vpc</code> from the dropdown list.</li><li>Under <strong>Availability Zones and subnets</strong>, select the private <a href=/install/aws/#subnets>subnets we created earlier</a> (<code>gitlab-private-10.0.1.0</code> and <code>gitlab-private-10.0.3.0</code>).</li><li>Select <strong>Next</strong>.</li></ol></li><li><p>In the Load Balancing settings section:</p><ol><li>Select <strong>Attach to an existing load balancer</strong>.</li><li>Select the target groups we created earlier in the <strong>Existing load balancer target groups</strong> dropdown list.</li><li>For <strong>Health Check Type</strong>, check the <strong>Turn on Elastic Load Balancing health checks</strong> option. We leave our <strong>Health Check Grace Period</strong> as the default <code>300</code> seconds.</li><li>Select <strong>Next</strong>.</li></ol></li><li><p>For <strong>Group size</strong>, set <strong>Desired capacity</strong> to <code>2</code>.</p></li><li><p>In the Scaling settings section:</p><ol><li>Select <strong>No scaling policies</strong>. The policies are configured later one.</li><li><strong>Min desired capacity:</strong> Set to <code>2</code>.</li><li><strong>Max desired capacity:</strong> Set to <code>4</code>.</li><li>Select <strong>Next</strong>.</li></ol></li><li><p>Finally, configure notifications and tags as you see fit, review your changes, and create the auto scaling group.</p></li><li><p>After the auto scaling group is created, we need to create a scale up and down policy in <a href=https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html>Cloudwatch</a> and assign them.</p><ol><li>Create an alarm for the <code>CPUUtilization</code> for metrics from <strong>EC2</strong> instances <strong>By Auto Scaling Group</strong> we created earlier.</li><li>Create a <a href=https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html#step-scaling-create-scale-out-policy>scale up policy</a> using the following conditions:<ol><li><strong>Add</strong> <code>1</code> capacity unit when <code>CPUUtilization</code> is greater than or equal to 60%.</li><li>Set the <strong>Scaling policy name</strong> to <code>Scale Up Policy</code>.</li></ol></li></ol><p><a href=/install/aws/img/scale_up_policy_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Scale Up Policy" src=/install/aws/img/scale_up_policy_v17_0.png></a></p><ol><li>Create a <a href=https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html#step-scaling-create-scale-in-policy>scale down policy</a> using the following conditions:<ol><li><strong>Remove</strong> <code>1</code> capacity unit when <code>CPUUtilization</code> is less than or equal to 45%.</li><li>Set the <strong>Scaling policy name</strong> to <code>Scale Down Policy</code>.</li></ol></li></ol><p><a href=/install/aws/img/scale_down_policy_v17_0.png target=_blank rel="noopener noreferrer"><img alt="Scale Down Policy" src=/install/aws/img/scale_down_policy_v17_0.png></a></p><ol><li>Assign the new dynamic scaling policy to the auto scaling group we created earlier.</li></ol></li></ol><p>As the auto scaling group is created, you see your new instances spinning up in your EC2 dashboard. You also see the new instances added to your load balancer. After the instances pass the heath check, they are ready to start receiving traffic from the load balancer.</p><p>Because our instances are created by the auto scaling group, go back to your instances and terminate the <a href=/install/aws/#install-gitlab>instance we created manually above</a>. We only needed this instance to create our custom AMI.</p><h2 id=health-check-and-monitoring-with-prometheus>Health check and monitoring with Prometheus</h2><p>Apart from Amazon CloudWatch, which you can enable on various services, GitLab provides its own integrated monitoring solution based on Prometheus. For more information about how to set it up, see <a href=/administration/monitoring/prometheus/>GitLab Prometheus</a>.</p><p>GitLab also has various <a href=/administration/monitoring/health_check/>health check endpoints</a> that you can ping and get reports.</p><h2 id=gitlab-runner>GitLab Runner</h2><p>If you want to take advantage of <a href=/ci/>GitLab CI/CD</a>, you have to set up at least one <a href=https://docs.gitlab.com/runner/>runner</a>.</p><p>Read more on configuring an <a href=https://docs.gitlab.com/runner/configuration/runner_autoscale_aws/>autoscaling GitLab Runner on AWS</a>.</p><h2 id=backup-and-restore>Backup and restore</h2><p>GitLab provides <a href=/administration/backup_restore/>a tool to back up</a> and restore its Git data, database, attachments, LFS objects, and so on.</p><p>Some important things to know:</p><ul><li>The backup/restore tool <strong>does not</strong> store some configuration files, like secrets; you must <a href=/administration/backup_restore/backup_gitlab/#storing-configuration-files>configure this yourself</a>.</li><li>By default, the backup files are stored locally, but you can <a href=/administration/backup_restore/backup_gitlab/#using-amazon-s3>backup GitLab using S3</a>.</li><li>You can <a href=/administration/backup_restore/backup_gitlab/#excluding-specific-data-from-the-backup>exclude specific directories form the backup</a>.</li></ul><h3 id=backing-up-gitlab>Backing up GitLab</h3><p>To back up GitLab:</p><ol><li><p>SSH into your instance.</p></li><li><p>Take a backup:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo gitlab-backup create</span></span></code></pre></div></div></li></ol><h3 id=restoring-gitlab-from-a-backup>Restoring GitLab from a backup</h3><p>To restore GitLab, first review the <a href=/administration/backup_restore/#restore-gitlab>restore documentation</a>, and primarily the restore prerequisites. Then, follow the steps under the <a href=/administration/backup_restore/restore_gitlab/#restore-for-linux-package-installations>Linux package installations section</a>.</p><h2 id=updating-gitlab>Updating GitLab</h2><p>GitLab releases a new version every month on the <a href=https://about.gitlab.com/releases/>release date</a>. Whenever a new version is released, you can update your GitLab instance:</p><ol><li><p>SSH into your instance</p></li><li><p>Take a backup:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo gitlab-backup create</span></span></code></pre></div></div></li><li><p>Update the repositories and install GitLab:</p><div class=codeblock-wrapper><div data-vue-app=codeblock-toolbar data-code-language=shell class=codeblock-toolbar></div><div class=highlight><pre tabindex=0 style=-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-shell data-lang=shell><span style=display:flex><span>sudo apt update </span></span><span style=display:flex><span>sudo apt install gitlab-ee</span></span></code></pre></div></div></li></ol><p>After a few minutes, the new version should be up and running.</p><h2 id=find-official-gitlab-created-ami-ids-on-aws>Find official GitLab-created AMI IDs on AWS</h2><p>Read more on how to use <a href=/solutions/cloud/aws/gitlab_single_box_on_aws/#official-gitlab-releases-as-amis>GitLab releases as AMIs</a>.</p><h2 id=conclusion>Conclusion</h2><p>In this guide, we went mostly through scaling and some redundancy options, your mileage may vary.</p><p>Keep in mind that all solutions come with a trade-off between cost/complexity and uptime. The more uptime you want, the more complex the solution. And the more complex the solution, the more work is involved in setting up and maintaining it.</p><p>Have a read through these other resources and feel free to <a href=https://gitlab.com/gitlab-org/gitlab/-/issues/new>open an issue</a> to request additional material:</p><ul><li><a href=/administration/reference_architectures/>Scaling GitLab</a>: GitLab supports several different types of clustering.</li><li><a href=/administration/geo/>Geo replication</a>: Geo is the solution for widely distributed development teams.</li><li><a href=https://docs.gitlab.com/omnibus/>Linux package</a> - Everything you must know about administering your GitLab instance.</li><li><a href=/administration/license/>Add a license</a>: Activate all GitLab Enterprise Edition functionality with a license.</li><li><a href=https://about.gitlab.com/pricing/>Pricing</a>: Pricing for the different tiers.</li></ul><h2 id=troubleshooting>Troubleshooting</h2><h3 id=instances-are-failing-health-checks>Instances are failing health checks</h3><p>If your instances are failing the load balancer&rsquo;s health checks, verify that they are returning a status <code>200</code> from the health check endpoint we configured earlier. Any other status, including redirects like status <code>302</code>, causes the health check to fail.</p><p>You may have to set a password on the <code>root</code> user to prevent automatic redirects on the sign-in endpoint before health checks pass.</p><h3 id=the-change-you-requested-was-rejected-422>&ldquo;The change you requested was rejected (422)&rdquo;</h3><p>If you see this page when trying to set a password via the web interface, make sure <code>external_url</code> in <code>gitlab.rb</code> matches the domain you are making a request from, and run <code>sudo gitlab-ctl reconfigure</code> after making any changes to it.</p><h3 id=some-job-logs-are-not-uploaded-to-object-storage>Some job logs are not uploaded to object storage</h3><p>When the GitLab deployment is scaled up to more than one node, some job logs may not be uploaded to <a href=/administration/object_storage/>object storage</a> properly. <a href=/administration/object_storage/#alternatives-to-file-system-storage>Incremental logging is required</a> for CI to use object storage.</p><p>Enable <a href=/administration/cicd/job_logs/#enable-or-disable-incremental-logging>incremental logging</a> if it has not already been enabled.</p></div><div class="help-feedback gl-rounded-base gl-mt-7 gl-p-5"><div class="help-feedback-container gl-flex"><div class=help-feedback-question-icon></div><button class="gl-flex gl-flex-row gl-items-center gl-justify-between" data-target=.feedback-wrapper data-toggle=collapse><h2 class="gl-m-0 gl-font-bold gl-text-lg">Help & feedback</h2><span class=help-feedback-toggle></span></button></div><div class="feedback-wrapper collapse"><div class="help-feedback-container xl:gl-flex"><div class="feedback gl-pr-5"><h3>Docs</h3><p><a href=https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/install/aws/_index.md>Edit this page</a> to fix an error or add an improvement in a merge request.</p><p><a href="https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Documentation">Create an issue</a> to suggest an improvement to this page.</p><h3>Product</h3><p><a href="https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue%5Bdescription%5D=Describe%20what%20you%20would%20like%20to%20see%20improved.%0A%0A%3C!--%20Don%27t%20edit%20below%20this%20line%20--%3E%0A%0A%2Flabel%20~%22docs%5C-comments%22%20&issue%5Btitle%5D=Docs%20-%20product%20feedback:%20Write%20your%20title">Create an issue</a> if there's something you don't like about this feature.</p><p><a href="https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20proposal%20-%20detailed&issue%5Btitle%5D=Docs%20feedback%20-%20feature%20proposal:%20Write%20your%20title">Propose functionality</a> by submitting a feature request.</p><h3>Feature availability and product trials</h3><p><a href=https://about.gitlab.com/pricing/>View pricing</a> to see all GitLab tiers and features, or to upgrade.</p><p><a href=https://about.gitlab.com/free-trial/>Try GitLab for free</a> with access to all features for 30 days.</p></div><div class=help><h3>Get help</h3><p>If you didn't find what you were looking for, <a href=/search/>search the docs</a>.</p><p>If you want help with something specific and could use community support, <a href="https://forum.gitlab.com/new-topic?title=topic%20title&body=topic%20body&tags=docs-feedback">post on the GitLab forum</a>.</p><p>For problems setting up or using this feature (depending on your GitLab subscription).</p><a class="btn btn-default btn-md gl-button" href=https://about.gitlab.com/support/>Request support</a></div></div></div></div></div><footer class=gl-pb-6><div class="gl-flex gl-justify-between gl-border-0 gl-border-t gl-border-solid gl-border-gray-100 gl-pt-5"><a href=https://gitlab.com/dashboard><img src=/gitlab-logo.svg alt="GitLab logo"></a><ul class="docs-social gl-list-none gl-flex"><li><a href=https://www.facebook.com/gitlab class=facebook><span class=gl-sr-only>Facebook</span></a></li><li><a href=https://www.linkedin.com/company/gitlab-com class=linkedin><span class=gl-sr-only>LinkedIn</span></a></li><li><a href=https://twitter.com/gitlab class=twitter><span class=gl-sr-only>Twitter</span></a></li><li><a href=https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg class=youtube><span class=gl-sr-only>YouTube</span></a></li></ul></div><ul class="docs-footer-links gl-list-none gl-pl-0 xl:gl-flex"><li class="gl-pr-5 gl-mb-2 xl:gl-mb-0"><a href=https://gitlab.com/gitlab-org/technical-writing/docs-gitlab-com class=gl-text-gray-600>Docs Repo</a></li><li class="gl-pr-5 gl-mb-2 xl:gl-mb-0"><a href=https://about.gitlab.com/company/ class=gl-text-gray-600>About GitLab</a></li><li class="gl-pr-5 gl-mb-2 xl:gl-mb-0"><a href=https://about.gitlab.com/terms/ class=gl-text-gray-600>Terms</a></li><li class="gl-pr-5 gl-mb-2 xl:gl-mb-0"><a href=https://about.gitlab.com/privacy/ class=gl-text-gray-600>Privacy Statement</a></li><li class="gl-pr-5 gl-mb-2 xl:gl-mb-0"><button id=ot-sdk-btn class=ot-sdk-show-settings>Cookie Settings</button></li><li class="gl-pr-5 gl-mb-2 xl:gl-mb-0"><a href=https://about.gitlab.com/company/contact/ class=gl-text-gray-600>Contact</a></li></ul><nav class=docs-edit-links aria-label=Footer><ul class="docs-footer-links-secondary gl-list-none gl-pl-0 gl-pb-5 xl:gl-flex"><li><a href=https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/install/aws/_index.md>View page source</a></li><li><a href=https://gitlab.com/-/ide/project/gitlab-org/gitlab/edit/master/-/doc/install/aws/_index.md>Edit in Web IDE</a></li><li><a href=https://creativecommons.org/licenses/by-sa/4.0/ class="gl-mb-5 md:gl-mb-0"><img alt="Creative Commons License" src=/by-sa.svg></a></li></ul></nav></footer></div><aside class=sidebar-right><div class="js-toc gl-text-base"></div></aside></div></main><script type=module src=/vite/main.js></script><script async>(function(e,t,n,s,o){e[s]=e[s]||[],e[s].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var a=t.getElementsByTagName(n)[0],i=t.createElement(n),r=s!="dataLayer"?"&l="+s:"";i.async=!0,i.src="https://www.googletagmanager.com/gtm.js?id="+o+r,a.parentNode.insertBefore(i,a)})(window,document,"script","dataLayer","GTM-NJXWQL")</script><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NJXWQL" height=0 width=0 style=display:none;visibility:hidden></iframe></noscript><script async>(function(){var e,t=!1;function n(){t===!1&&(t=!0,Munchkin.init("194-VVC-221",{useBeaconAPI:!0}))}e=document.createElement("script"),e.type="text/javascript",e.async=!0,e.src="https://munchkin.marketo.net/munchkin.js",e.onreadystatechange=function(){(this.readyState=="complete"||this.readyState=="loaded")&&n()},e.onload=n,document.getElementsByTagName("head")[0].appendChild(e)})()</script><script async src=https://cdn.bizible.com/scripts/bizible.js></script><script async>_linkedin_partner_id="30694",window._linkedin_data_partner_ids=window._linkedin_data_partner_ids||[],window._linkedin_data_partner_ids.push(_linkedin_partner_id),function(){var t=document.getElementsByTagName("script")[0],e=document.createElement("script");e.type="text/javascript",e.async=!0,e.src="https://snap.licdn.com/li.lms-analytics/insight.min.js",t.parentNode.insertBefore(e,t)}()</script><noscript><img height=1 width=1 style=display:none alt src="https://dc.ads.linkedin.com/collect/?pid=30694&fmt=gif"></noscript><script src=https://cdn.jsdelivr.net/npm/@gitlab/application-sdk-browser@0.2.8/dist/gl-sdk.min.js></script><script>const GL_PRODUCT_ANALYTICS_JSON={appId:"e1c8d446-8edf-46fa-9e6a-9f964b8675c8",host:"https://collector.prod-1.gl-product-analytics.com",hasCookieConsent:!0};Object.values(GL_PRODUCT_ANALYTICS_JSON).includes("")||(window.glClient=window.glSDK.glClientSDK(),window.glClient?.page())</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10