{"id":18623604,"url":"https://github.com/codica2/gitlab-bastion-ci-aws","last_synced_at":"2026-02-19T09:32:13.176Z","repository":{"id":98303149,"uuid":"195967678","full_name":"codica2/gitlab-bastion-ci-aws","owner":"codica2","description":"Cost-effective and scalable solution for you CI/CD","archived":false,"fork":false,"pushed_at":"2022-11-13T23:41:34.000Z","size":172,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-12-16T01:26:11.166Z","etag":null,"topics":["cd","ci","gitlab","gitlab-runner"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codica2.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-09T08:35:52.000Z","updated_at":"2022-11-13T23:41:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"87467a7a-2c1d-4b09-889b-d8116a9650fb","html_url":"https://github.com/codica2/gitlab-bastion-ci-aws","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/codica2/gitlab-bastion-ci-aws","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codica2%2Fgitlab-bastion-ci-aws","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codica2%2Fgitlab-bastion-ci-aws/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codica2%2Fgitlab-bastion-ci-aws/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codica2%2Fgitlab-bastion-ci-aws/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codica2","download_url":"https://codeload.github.com/codica2/gitlab-bastion-ci-aws/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codica2%2Fgitlab-bastion-ci-aws/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29609524,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T06:47:36.664Z","status":"ssl_error","status_checked_at":"2026-02-19T06:45:47.551Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cd","ci","gitlab","gitlab-runner"],"created_at":"2024-11-07T04:25:18.276Z","updated_at":"2026-02-19T09:32:13.170Z","avatar_url":"https://github.com/codica2.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eAutoscaling GitLab Runner on AWS\u003c/h1\u003e\n\n![](gitlab-docker-amazon.jpg)\n\nIf you want to have a cost-effective and scalable solution for you CI/CD builds, it can be suitable to use Gitlab Runner with it's autoscaling feature.\nIn this example, we'll show how to configure a Gitlab Runner in AWS that will serve as the bastion where it will spawn new spot instances whenever you will need.\n\n## Prerequisites\n\nWe will this tools:\n* [Gitlab Runner](https://docs.gitlab.com/runner/)\n* [Amazon’s EC2 Spot Instances](https://aws.amazon.com/ec2/spot/) \n* [Docker Machine](https://docs.docker.com/machine/drivers/aws/)\n\n## Prepare the bastion instance\n\nThe first step is to install GitLab Runner in an EC2 instance that will serve as the bastion that spawns new machines. This doesn’t have to be a powerful machine since it will not run any jobs itself, a t3.micro instance will do. This machine will be a dedicated host since we need it to always be up and running, thus it will be the only standard monthly cost.\n\nInstall the prerequisites:\n\n* [Install GitLab Runner from the official GitLab repository](https://docs.gitlab.com/runner/install/linux-repository.html)\n* [Install Docker](https://docs.docker.com/install/)\n* [Install Docker Machine](https://docs.docker.com/machine/install-machine/)\n* [Registering the GitLab Runner](https://docs.gitlab.com/runner/register/)\n\nAfter the Runner is registered, we need to edit it's configuration file and add the required options for the AWS docker machine driver.\n\n# The Gitlab Runner configuration\n\n## The main section\n\nIn the main section, we can define the limit of the jobs that can be run in parallel across all Runners (`concurrent`). We can start with something low like 2, and increase or decrease when we need.\n\nThe `check_interval` option defines how often the Runner should check GitLab for new jobs (in seconds).\n\nExample:\n\n```yaml\nconcurrent = 2\ncheck_interval = 0\n```\n\n[Read more](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section) about all the options we can use.\n\n## The runners section\n\nFrom the  `[[runners]]`  section, the most important part is the executor which must be set to docker+machine. Most of those settings are taken care of when you register the Runner for the first time.\n\n `limit` sets the maximum number of machines (running and idle) that this Runner will spawn. For more info check the relationship between  `limit`, `concurrent` and `IdleCount`.\n\nExample:\n\n```yaml\n[[runners]]\n  name = \"gitlab-aws-autoscaler\"\n  url = \"\u003cURL of your GitLab instance\u003e\"\n  token = \"\u003cRunner's token\u003e\"\n  executor = \"docker+machine\"\n  limit = 20\n```\n\n[Read more](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) about all the options we can use.\n\n## The runners.docker section\n\nIn this section, we can define the default Docker image to be used by the child Runners if it’s not defined in  `.gitlab-ci.yml`. To build our apps we use Kaniko executor and we don't need to use privileged mode. [Read more](https://www.trendmicro.com/en_us/research/19/l/why-running-a-privileged-container-in-docker-is-a-bad-idea.html) why priviliged is dangerous.\n\nNext, we use `disable_cache = true` to disable the Docker executor’s inner cache mechanism since we will use the distributed cache mode as described in the following section.\n\nExample:\n\n```yaml\n  [runners.docker]\n    tls_verify = false\n    image = \"alpine\"\n    privileged = false\n    disable_entrypoint_overwrite = false\n    oom_kill_disable = false\n    disable_cache = true\n    shm_size = 0\n```\n\n[Read more](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section) about all the options we can use under `[runners.docker]`.\n\n## The runners.cache section\n\nTo speed up our jobs, GitLab Runner provides a cache mechanism. It is recommended to use the distributed cache mechanism that GitLab Runner provides. Since new instances will be created on demand, it is essential to have a common place where the cache is stored.\nIn next example we don't use ACCESS_KEY or SECRET_KEY. Gitlab-runner will look on our IAM Role on our bastion.\nIn the following example, we use Amazon S3:\n\n```yaml\n [runners.cache]\n    Type = \"s3\"\n    Shared = true\n    [runners.cache.s3]\n      BucketName = \"\u003cthe bucket where your cache should be kept\u003e\"\n      BucketLocation = \"us-east-1\"\n```\n\n## The runners.machine section\n\nThis is the most important part of the configuration and it’s the one that tells GitLab Runner how and when to spawn new or remove old Docker Machine instances.\n\n```yaml\n  [runners.machine]\n    IdleCount = 0\n    IdleTime = 120\n    MaxBuilds = 100\n    MachineDriver = \"amazonec2\"\n    MachineName = \"gitlab-docker-machine-%s\"\n    MachineOptions = [\n      \"amazonec2-iam-instance-profile=nameOfRole\", # Better security features. We can use IAM Roles instead of AWS keys\n      \"amazonec2-region=XXXXX\", \n      \"amazonec2-vpc-id=vpc-XXXXX\", \n      \"amazonec2-subnet-id=subnet-XXXXX\", \n      \"amazonec2-zone=a\", \n      \"amazonec2-tags=runner-manager-name,gitlab-aws-autoscaler,gitlab,true,gitlab-runner-autoscale,true\", \n      \"amazonec2-security-group=XXXXX\", \n      \"amazonec2-instance-type=t3.medium\", \n      \"amazonec2-request-spot-instance=true\", \n      \"amazonec2-spot-price=0.03\"\n      ]\n    OffPeakPeriods = [\"* * 0-8,22-23 * * mon-fri *\", \"* * * * * sat,sun *\"]\n    OffPeakTimezone = \"Europe/Kiev\"\n    OffPeakIdleCount = 0\n    OffPeakIdleTime = 120\n```\n\n[Read more](https://docs.gitlab.com/runner/configuration/runner_autoscale_aws/#the-runnersmachine-section) about MachineOptions.\n\n## Use IAM Roles instead of AWS_SECRET_KEY and AWS_ACCESS_KEY\n\nFirst of all, we need to create `IAM Role` for out bastion server. Below you can see which permissions for IAM Role you need.  \n[Read more about IAM Roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) . \nExample for creating EC2-spot instances(it uses on `bastion server`):\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": [\n                \"ec2:DescribeKeyPairs\",\n                \"ec2:TerminateInstances\",\n                \"ec2:StopInstances\",\n                \"ec2:StartInstances\",\n                \"ec2:RunInstances\",\n                \"ec2:RebootInstances\",\n                \"ec2:CreateKeyPair\",\n                \"ec2:DeleteKeyPair\",\n                \"ec2:ImportKeyPair\",\n                \"ec2:Describe*\",\n                \"ec2:CreateTags\",\n                \"ec2:RequestSpotInstances\",\n                \"ec2:CancelSpotInstanceRequests\",\n                \"ec2:DescribeSubnets\",\n                \"ec2:AssociateIamInstanceProfile\"\n            ],\n            \"Effect\": \"Allow\",\n            \"Resource\": \"*\"\n        },\n        {\n            \"Action\": [\n                \"iam:PassRole\"\n            ],\n            \"Effect\": \"Allow\",\n            \"Resource\": \"*\",\n            \"Condition\": {\n                \"IpAddress\": {\n                    \"aws:SourceIp\": \"\u003cuse your EIP on bastion\u003e\" # It means that you can attach role to resource only from IP that belongs to bastion instance.\n                }\n            }\n        }\n    ]\n}\n```\nNext step, you need to create IAM Role for your runners. Your permissions will depend on your CI/CD process.  \nBelow, permission for our runners with our CI/CD workflow:\n```json\n\n{\n  # This permission we are using for UpdateService in deploy stage in our CI/CD process.\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"ecs:UpdateService\"\n            ],\n            \"Resource\": [\n                \"arn:aws:ecs:region:account-id:service/name_of_cluster/name_of_service\",\n                \"arn:aws:ecs:region:account-id:service/name_of_cluster/name_of_service\"\n            ]\n        }\n    ]\n}\n```\nPolicy to push images to ECR `push our image to ECR repositories`:\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"ecr:CompleteLayerUpload\",\n                \"ecr:UploadLayerPart\",\n                \"ecr:InitiateLayerUpload\",\n                \"ecr:BatchCheckLayerAvailability\",\n                \"ecr:PutImage\",\n                \"ecr:GetDownloadUrlForLayer\",\n                \"ecr:BatchGetImage\"\n            ],\n            \"Resource\": [\n                \"arn:aws:ecr:region:account-id:repository/name_of_repo\",\n                \"arn:aws:ecr:region:account-id:repository/name_of_repo\"\n            ]\n        },\n        {\n            \"Action\": [\n                \"ecr:GetAuthorizationToken\"\n            ],\n            \"Effect\": \"Allow\",\n            \"Resource\": \"*\"\n        }\n    ]\n}\n```\nPolicy to Describe Target Health on our `ELB`:\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": \"elasticloadbalancing:DescribeTargetHealth\",\n            \"Resource\": \"*\"\n        }\n    ]\n}\n```\n\n## Getting it all together\n\nHere’s the full example of `/etc/gitlab-runner/config.toml`:\n\n```yaml\nconcurrent = 10\ncheck_interval = 0\n\n[[runners]]\n  name = \"gitlab-aws-autoscaler\"\n  url = \"\u003cURL of your GitLab instance\u003e\"\n  token = \"\u003cRunner's token\u003e\"\n  executor = \"docker+machine\"\n  limit = 20\n  [runners.cache]\n    [runners.cache.s3]\n      BucketName = \"\u003cthe bucket where your cache should be kept\u003e\"\n      BucketLocation = \"us-east-1\"\n  [runners.docker]\n    tls_verify = false\n    image = \"alpine\"\n    privileged = false\n    disable_entrypoint_overwrite = false\n    oom_kill_disable = false\n    disable_cache = true\n    shm_size = 0\n  [runners.machine]\n    IdleCount = 0\n    IdleTime = 120\n    MaxBuilds = 100\n    MachineDriver = \"amazonec2\"\n    MachineName = \"gitlab-docker-machine-%s\"\n    MachineOptions = [\n      \"amazonec2-iam-instance-profile=nameOfRole\",\n      \"amazonec2-region=XXXXX\", \n      \"amazonec2-vpc-id=vpc-XXXXX\", \n      \"amazonec2-subnet-id=subnet-XXXXX\", \n      \"amazonec2-zone=a\", \n      \"amazonec2-tags=runner-manager-name,gitlab-aws-autoscaler,gitlab,true,gitlab-runner-autoscale,true\", \n      \"amazonec2-security-group=XXXXX\", \n      \"amazonec2-instance-type=t3.medium\", \n      \"amazonec2-request-spot-instance=true\", \n      \"amazonec2-spot-price=0.03\"\n      ]\n```\n\n[Read the official documentation](https://docs.gitlab.com/runner/configuration/runner_autoscale_aws/#introduction) about Autoscaling GitLab Runner on AWS.\n\n## License\n\nCopyright © 2015-2022 Codica. It is released under the [MIT License](https://opensource.org/licenses/MIT).\n\n## About Codica\n\n[![Codica logo](https://www.codica.com/assets/images/logo/logo.svg)](https://www.codica.com)\n\nThe names and logos for Codica are trademarks of Codica.\n\nWe love open source software! See [our other projects](https://github.com/codica2) or [hire us](https://www.codica.com/) to design, develop, and grow your product.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodica2%2Fgitlab-bastion-ci-aws","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodica2%2Fgitlab-bastion-ci-aws","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodica2%2Fgitlab-bastion-ci-aws/lists"}