{"id":21072450,"url":"https://github.com/binarymist/aws-docker-host","last_synced_at":"2026-01-02T04:42:50.537Z","repository":{"id":69462546,"uuid":"109775293","full_name":"binarymist/aws-docker-host","owner":"binarymist","description":":truck: Use Terraform to pack a sinlge free AWS EC2 instance with Docker containers","archived":false,"fork":false,"pushed_at":"2017-11-08T07:07:57.000Z","size":59,"stargazers_count":11,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-20T22:12:32.487Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/binarymist.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},"funding":{"github":["binarymist"]}},"created_at":"2017-11-07T02:16:54.000Z","updated_at":"2023-04-14T08:42:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"dbe9be82-0ade-46f9-acb8-f9b8cbf7b2fb","html_url":"https://github.com/binarymist/aws-docker-host","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binarymist%2Faws-docker-host","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binarymist%2Faws-docker-host/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binarymist%2Faws-docker-host/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binarymist%2Faws-docker-host/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/binarymist","download_url":"https://codeload.github.com/binarymist/aws-docker-host/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243515569,"owners_count":20303258,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-11-19T18:56:35.743Z","updated_at":"2026-01-02T04:42:50.497Z","avatar_url":"https://github.com/binarymist.png","language":"HCL","funding_links":["https://github.com/sponsors/binarymist"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cbr/\u003e\n  \u003ca href=\"https://binarymist.io\" title=\"BinaryMist\"\u003e\n    \u003cimg width=1024px src=\"Logo.png\" alt=\"aws-docker-host logo\"\u003e\n  \u003c/a\u003e\n  \u003cbr/\u003e\n\u003c/div\u003e\n\u003cbr/\u003e\u003cbr/\u003e\n\n# Licence\n\nThis project is licensed under the `BSD 3-clause “New” or “Revised” License`. For more information please see the licence file\n\n# Synopsis\n\nThe purpose of this project is to take advantage of the AWS free tier, specifically T2.Micro EC2 instances at this stage. The project in it's current state sets up a T2.Micro instance, along with a reasonable security profile, pushes a single docker container to the instance and runs it.\n\nThere is no reason why you couldn't setup many AWS accounts and run aws-docker-host against all of them, giving you many **free** EC2 hosts packed full of running Docker containers.\n\nNext step is to push many containers to the same single instance, I haven't needed this yet, but probably will soon, which is pretty much just a matter of iterating on the [`aws_instance`](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/instance_creation/instance.tf#L32) resource (as commented in the code).\n\naws-docker-host also adds or modifies an existing CloudFlare A record once your new instance is up and has its elastic IP, so that your DNS keeps pointing to the new instance each time `terraform apply` is run.\n\nNo need to store your Docker images in a registry, `terraform apply` [builds](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/instance_creation/instance.tf#L44) your app or service image locally, [tars the image](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/instance_creation/instance.tf#L47), [pushes image](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/instance_creation/instance.tf#L61) to the new EC2 instance, [installs](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/instance_creation/instance.tf#L80) the same version of Docker that you tested locally, [loads](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/instance_creation/instance.tf#L89) the Docker image, [runs](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/instance_creation/instance.tf#L91) the image as the container name you specify with `--restart=unless-stopped` so that you can be sure your app will always be running.\n\n# Usage\n\nFor deployment of a docker host using Terraform. Go through this up front, then just before free tier period (one year) runs out, use the following again. Some steps can be left out if the free period has not yet run out, like there is nothing to stop you setting this infrastructure up (`terraform plan` -\u003e `terraform apply`) and tearing it down (`terraform destroy`) repeatedly. Just keep in mind, that every time an instance is started it's classed as an instance hour, and you only get 750 hours per month per account, nothing to stop you setting up many accounts though:\n\nMake sure [Terraform is installed](https://www.terraform.io/intro/getting-started/install.html):\n\n1. Download the zip\n2. Verify the checksum\n3. Extract to a place on your path:  \n  * `sudo unzip ~/Downloads/terraform_0.10.6_linux_amd64.zip -d /opt/`\n  * `sudo ln -s /opt/terraform /usr/bin/terraform`\n\nMake sure Docker is installed locally, ideally with same version that was last used to setup the infrastructure (detailed in [aws-docker-host/tf/variables_override.tf](https://github.com/binarymist/aws-docker-host/blob/77429d31dc460e4c438fa6399975257af3eb6724/tf/variables_override-example#L57) tested_docker_version).\n\n1. Create gmail account (terraformedawsdocker\u003cn\u003e@gmail.com) for example  \n   * Setup 2FA and Backup codes  \n   \n2. Create AWS account (root user) terraformedawsdocker\u003cn\u003e@gmail.com  \n   * Use dummy info  \n   \n3. Create AWS IAM user terraformedawsdocker\u003cn\u003e from root account  \n   * This account has AdministratorAccess managed policy\n   * This account had cli turned on, so I could run Terraform initially, cli is not needed on this user as I now use a lower privilged user (see next step), and cli key is deactivated. When you crate this user, you can deactivate cli key  \n   \n4. Create AWS IAM user terraformedawsdocker\u003cn\u003e-cli from terraformedawsdocker\u003cn\u003e user above  \n   * CLI access only\n   * Created group `docker\u003cn\u003e-cli`\n   * Added Policies: `AmazonEC2FullAccess` and `IAMFullAccess` (need for IAM role we add in [aws-docker-host/tf/iam/user.tf](https://github.com/binarymist/aws-docker-host/blob/aefe2eba9e953a93c248fd42f7c5be94a1c54021/tf/iam/user.tf))  \n\n   This user runs the `terraform [ plan | apply | destroy]`. Access key id and secret access key need to be updated in aws-docker-host/tf/variables_override.tf access_key and secret_key  \n   While you're there, update the cloudflare_email and cloudflare_token if they have changed (unlikely), along with any other values in the variables_override.tf file, also make sure you have generated your SSH key-pair as I explain [here](http://f1.holisticinfosecforwebdevelopers.com/chap03.html#vps-countermeasures-disable-remove-services-harden-what-is-left-ssh-hardening-ssh) in my book. Just rename the [`variables_override-example`](https://github.com/binarymist/aws-docker-host/blob/f62f757da2b2529784e1ea3f90a20cedea11a72f/tf/variables_override-example) to `variables_override.tf` add your configurations, and `chmod 600`, keep it safe and don't commit it to source control  \n   \n5. `terraform init` will be needed on any new development machine to load modules  \n   * Then it's just a matter of `terraform plan` -\u003e `terraform apply`, and `terraform destroy` if you want to have another go.\n\nCheck CloudFlare record to satisfy yourself that the new elastic IP has been added as the `A` record for your domain.\n\nIf this is the second time you're doing this with a new AWS account, once you have a successful deployment:\n\n1. Upgrade Terraform to latest version\n2. Upgrade Docker to latest stable version\n3. Deploy again -\u003e test\n\nWhen happy, turn services off on last years AWS account, and bingo, you have another free year, with very little work.\n\n# For ad-hoc or continuous deployment of individual containers\n\nFor example, once you have already ran `terraform apply` and your infrastructure is setup, you can very easily just run `terraform destroy` -\u003e `terraform plan` -\u003e `terraform apply` again, but if you have other containers running on your **free** EC2 instance, this is overkill, so I usually just do the following:\n\nbuild image:\n\n```bash\ndocker build --tag \u003cimage_name\u003e\n```\n\nTest locally:\n\n```bash\ndocker run -e \"NODE_ENV=development\" -p 80:3000 -it --name \u003ccontainer_name\u003e \u003cimage_name\u003e\n```\n\n```bash\n# gives the container 10 seconds to stop it's service gracefully, if it doesn't, then it is forcefully stopped.\ndocker stop \u003ccontainer_name\u003e\n```\n\n```bash\ndocker start \u003ccontainer_name\u003e\n```\n\nYou can deploy with a set of scripts similar to the following:\n\nremoteWork\n\n```bash\n#!/bin/bash\n\n# The following commands should be similar to those run in terraform.\n\nif [ -e \"$remote_tarred_image_path\" ] ; then  \n  echo \"Stopping container: $container_name\"\n  echo \"$(docker stop $container_name)\"\n  echo \"Removing container: $container_name\"\n  echo \"$(docker rm $container_name)\"\n  echo \"Loading image from: $remote_tarred_image_path\"\n  echo \"$(docker load -i $remote_tarred_image_path)\"\n  echo \"Running container: $container_name from image: $image_name\"\n  echo \"$(docker run -e \\\"NODE_ENV=production\\\" -p 80:3000 -d --restart=unless-stopped --name $container_name $image_name)\"\nelse\n  echo \"The tarred image: $remote_tarred_image_path does not exist. No upgrade performed.\"\nfi\n\necho \"Waiting 5 seconds...\"\nsleep 5\necho \"Currently running containers...\"\necho \"$(docker ps -a)\"\nrm $remote_tarred_image_path 2\u003e/dev/null\n```\n\nimg-deploy\n\n```bash\n#!/bin/bash\n\n# Check aws-docker-host for settings.\n# Clean out docker images from time to time on targetServer.\n\nreadonly instance_user=\"\u003cuser name\u003e\"\nreadonly docker_image_name=\"\u003cyour image name\u003e\"\nreadonly image_app_content=\"\u003cwhere your source is\u003e\"\nreadonly local_tarred_docker_image_path=\"\u003csource path of your tarred image\u003e\"\nreadonly targetServer=\"\u003caddress of existing elastic IP\u003e\"\nreadonly sshPort=\"\u003cssh port of target instance\u003e\"\nreadonly private_key_file_path=\"/home/you/.ssh/id_rsa\"\nreadonly remote_tarred_docker_image_path=\"\u003ctarget path of your tarred image\u003e\"\nreadonly docker_container_name=\"\u003cwhat ever you want to tag your container as\u003e\"\n\nbuild_and_tar_image() {\n   docker build --tag $docker_image_name $image_app_content\n   echo \"Tarring $docker_image_name\"\n   docker save -o $local_tarred_docker_image_path $docker_image_name\n   echo \"docker save produced file: $(ls $local_tarred_docker_image_path -liah)\"\n}\n\nscp_tarred_img() {\n   echo\n   #scp -i $private_key_file_path -qv -P $sshPort $local_tarred_docker_image_path $instance_user@$targetServer:~ 2\u003e\u00261 | grep -v debug\n   # Rsync gives us nice progress meter.\n   echo \"Rsync copying file...\"\n   rsync -v --progress -e \"ssh -i $private_key_file_path -p $sshPort\" $local_tarred_docker_image_path $instance_user@$targetServer:$remote_tarred_docker_image_path \n   declare -r result=$?\n   return $result\n}\n\nremote_host_work() {\n   echo\n   echo \"Performing remote host work now ...\"\n   echo\n   declare -r result=$(/usr/bin/ssh $instance_user@$targetServer -p $sshPort \\\n      remote_tarred_image_path=$remote_tarred_docker_image_path \\\n      container_name=$docker_container_name \\\n      image_name=$docker_image_name \\\n      'bash -s' \u003c \"./remoteWork\")\n   echo \"$result\"\n}\n\n\nbuild_and_tar_image\nscp_tarred_img\nremote_host_work\n```\n\nNow just run the following once you've turned the executable bit on:\n\n```bash\n./img-deploy\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinarymist%2Faws-docker-host","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbinarymist%2Faws-docker-host","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinarymist%2Faws-docker-host/lists"}