Skip to content

Instantly share code, notes, and snippets.

@OleksandrPoltavets
Last active July 4, 2024 18:37
Show Gist options
  • Save OleksandrPoltavets/274a4e1f7c9c5abd78bd7da9ca12d5a1 to your computer and use it in GitHub Desktop.
Save OleksandrPoltavets/274a4e1f7c9c5abd78bd7da9ca12d5a1 to your computer and use it in GitHub Desktop.
Rails 7.1 + Docker + PG + AWS EC2 + NGINX
POSTGRES_DB=myapp_production
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_HOST=db
RAILS_ENV=production
RAILS_MASTER_KEY=your_master_key_here

Rails 7.1 + Docker deployment to AWS EC2

Step by step guide

1. Create new Rails APP

  • rails new dockerable --api -d postgresql
  • rails db:create
  • rails generate scaffold Post title:string body:text
  • rails db:migrate
  • rails db:seed

2. Configure GIT

  • cd /path/to/your/rails_project
  • git init
  • git remote add origin git@github.com:OleksandrPoltavets/dockerable.git
  • git add .
  • git commit -m "Initial commit"
  • git branch -M main
  • git push -u origin main

3. Verify app via HTTP (curl or Postman) [optional]

  • curl http://localhost:3000/posts
  • curl -X POST -H "Content-Type: application/json" -d '{"post": {"title": "New Post", "body": "This is the body of the new post."}}' http://localhost:3000/posts

4. Configure Dockerfile

  • see Dockerfile file withing this gist

4. Configure docker-compose.yml

  • see docker-compose.yml file withing this gist

5. ENV file

  • see .ENV file withing this gist
  • RAILS_MASTER_KEY value is the value of config/master.key file

6. Docker build & run (localhost)

Don't forget to disable SSL (config.force_ssl = true) for production if you don't have it configured locally and want to run rails server in production env.

  • docker-compose build
  • docker-compose up or docker-compose up -d
  • [optional] docker-compose exec web rails console <- run rails console inside docker container

You should be able to see Rails server up and running locally on http://localhost:3000

7. AWS EC2 linux server

  • EC2 > Instances > Launch an instance > Ubuntu 24.04

  • Generate and save .pem key to your development environment: dockerable-ec2.pem

  • enable http/https web access 0.0.0.0/0

  • SSH to the EC2 server

    • sudo ssh -i dockerable-ec2.pem ubuntu@your-ec2-public-ip
  • Install Docker / Docker Compose on Ubuntu server

  • Generate an SSH Key Pair (for GitHub)

    • sudo ssh-keygen -t rsa
  • Add the SSH Key to Your GitHub Account (general settings)

    • sudo cat ~/.ssh/id_rsa.pub
  • sudo git clone git@github.com:OleksandrPoltavets/dockerable.git

  • cd dockerable

  • sudo nano .env <- paste all EVN variables

  • sudo docker-compose build

  • sudo docker-compose up -d # start/stop/remove

  • sudo docker-compose ps <- to see status

  • sudo docker-compose logs web / docker-compose logs db

  • Expose port 3000 to the World !!!

    • EC2 instance -> Security Groups -> Edit inbound rules -> Add Rule -> Custom TCP -> port 3000 : 0.0.0.0/0

8. NGINX

  • sudo apt install nginx
  • sudo nano /etc/nginx/sites-available/dockerable <- add nginx config and save file
  • sudo ln -s /etc/nginx/sites-available/dockerable /etc/nginx/sites-enabled/
  • sudo rm /etc/nginx/sites-enabled/default
  • sudo service nginx restart

9. CI/CD GitHub Actions (BONUS)

  • Set Up SSH Access from GitHub Actions to EC2
    • ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
    • filename ~/.ssh/github_actions
  • Copy the PUBLIC .pub Key to Your EC2 Instance
    • ssh-copy-id -i ~/.ssh/github_actions.pub ubuntu@your-ec2-public-ip
    • make sure you received: Number of key(s) added: 1
  • Add the PRIVATE Key + other data to GitHub Secrets
    • repository on GitHub Settings > Secrets and variables > Actions
    • name: EC2_SSH_KEY and add public key from cat .ssh/github_actions
    • name: EC2_HOST, value: your-ec2-public-ip
    • name: EC2_USER, value: ubuntu
  • Create a GitHub Actions Workflow
    • in Github repo create a directory .github/workflows
    • in .github/workflows create a file deploy.yml <- see example withing current gist
  • Deploy Changes
    • git add .
    • git commit -m "Set up GitHub Actions for deployment"
    • git push origin main
    • check Github actions for any errors for the latest commit (this commit should trigger the deployment to EC2)
  • ENJOY!
name: Deploy to EC2
on:
push:
branches:
- main # Change to your main branch name if different
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
echo "Host ec2-instance" >> ~/.ssh/config
echo " HostName ${{ secrets.EC2_HOST }}" >> ~/.ssh/config
echo " User ${{ secrets.EC2_USER }}" >> ~/.ssh/config
echo " IdentityFile ~/.ssh/id_rsa" >> ~/.ssh/config
echo " StrictHostKeyChecking no" >> ~/.ssh/config
- name: Archive the repository contents
run: |
mkdir -p /tmp/repo
cp -r . /tmp/repo
tar -czf /tmp/repo.tar.gz -C /tmp repo
- name: Copy archive to EC2
run: scp -o StrictHostKeyChecking=no /tmp/repo.tar.gz ec2-instance:/home/ubuntu/dockerable.tar.gz
- name: Deploy on EC2
run: |
ssh ec2-instance << 'EOF'
cd /home/ubuntu
mkdir -p dockerable
tar -xzf dockerable.tar.gz -C dockerable --strip-components=1
cd dockerable
docker-compose down
docker-compose build
docker-compose up -d
sudo systemctl restart nginx
EOF
services:
db:
image: postgres:14.2-alpine
container_name: demo-postgres-14.2
volumes:
- postgres_data:/var/lib/postgresql/data
command:
"postgres -c 'max_connections=500'"
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
ports:
- "5432:5432"
demo-web:
build: .
command: "./bin/rails server"
environment:
- RAILS_ENV=${RAILS_ENV}
- POSTGRES_HOST=${POSTGRES_HOST}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
volumes:
- app-storage:/rails/storage
depends_on:
- db
ports:
- "3000:3000"
volumes:
postgres_data: {}
app-storage: {}
# syntax = docker/dockerfile:1
ARG RUBY_VERSION=3.3.0
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
# Rails app lives here
WORKDIR /rails
# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
# Throw-away build stage to reduce size of final image
FROM base as build
# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libpq-dev libvips pkg-config
# Install application gems
COPY Gemfile Gemfile.lock ./
RUN gem install rails && \
bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
# Copy application code
COPY . .
# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/
# Final stage for app image
FROM base
# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libvips postgresql-client && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
USER rails:rails
# Ensure the Rails bin directory is in the PATH
ENV PATH="/rails/bin:/usr/local/bundle/bin:${PATH}"
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]
server {
listen 80;
server_name _;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment