Skip to content

Instantly share code, notes, and snippets.

@loftwah
Last active May 20, 2025 02:33
Show Gist options
  • Select an option

  • Save loftwah/ed109940427e89010d344cf6748fbf03 to your computer and use it in GitHub Desktop.

Select an option

Save loftwah/ed109940427e89010d344cf6748fbf03 to your computer and use it in GitHub Desktop.
GitHub Actions and Environments

GitHub Actions and Environments

Key Points

  • It seems likely that using GitHub Actions with Docker and Postgres for dev, staging, and prod environments is a solid approach, especially with Prisma for database management.
  • Research suggests that different branches for each environment (e.g., dev, staging, prod) and environment-specific configurations can streamline CI/CD processes.
  • The evidence leans toward using environmental variables and secrets for managing database connections securely across environments.

Overview

Setting up development (dev), staging, and production (prod) environments for continuous integration and continuous deployment (CI/CD) using GitHub Actions, Docker, Postgres, and Prisma can be streamlined with a structured approach. This setup ensures isolation, security, and ease of deployment, particularly when using different branches for each environment. Below, we’ll break down the process into manageable steps, focusing on simplicity and best practices.

Environment Setup

  • Create GitHub Environments: Start by defining dev, staging, and prod environments in your GitHub repository settings under "Environments." This helps manage deployment targets and can include protection rules like required reviewers for prod deployments.
  • Database Configuration: Use a single Postgres container with separate databases for each environment (e.g., your-app-dev, your-app-staging, your-app-prod) for simplicity, or use separate containers for more isolation. Create users and grant access for each database to ensure security.

Docker and Prisma Integration

  • Containerize Your Application: Use Docker to build an image that includes your application and Prisma. Ensure your Dockerfile runs Prisma migrations (npx prisma migrate deploy) before starting the application, ensuring database schema updates are applied automatically.
  • Configure Prisma: Set your schema.prisma file to use an environmental variable for the database URL (e.g., url = env("DATABASE_URL")), allowing dynamic connection to the appropriate database for each environment.

CI/CD with GitHub Actions

  • Workflow Setup: Create a GitHub Actions workflow that builds and tests on every push, then deploys to the corresponding environment based on the branch (e.g., dev branch to dev, master to prod). Use SSH to deploy to your server (e.g., AWS EC2) and run docker-compose up for the specific service.
  • Manage Secrets: Use GitHub Actions secrets for sensitive information like database credentials, setting environment-specific secrets (e.g., DATABASE_URL_DEV, DATABASE_URL_PROD) to ensure secure connections.

Best Practices

  • Use environmental variables in your docker-compose.yml to set DATABASE_URL for each service, ensuring the application connects to the correct database.
  • Run migrations as part of the container startup to keep database schemas in sync with your application code, but consider separate migration steps for production to avoid downtime.
  • Ensure a branch strategy (e.g., feature → dev → staging → prod) to manage changes systematically, with testing at each stage.

This approach should help you manage your environments effectively, though for production, consider using managed database services for enhanced security and scalability.


Survey Note: Detailed Implementation Guide for Multi-Environment CI/CD with GitHub Actions, Docker, Postgres, and Prisma

This comprehensive guide expands on the setup for development, staging, and production environments using GitHub Actions, Docker, Postgres, and Prisma, ensuring a robust CI/CD pipeline. It incorporates best practices and detailed configurations, drawing from extensive research and documentation to address your needs for easy management and deployment.

Introduction

Modern software development requires isolated environments to test and deploy changes safely. By leveraging GitHub Actions for automation, Docker for containerization, Postgres for database management, and Prisma as an ORM, you can create a streamlined workflow from dev to staging to prod. This guide assumes you’re using different branches for each environment, a common and effective strategy for CI/CD.

System Architecture and Environment Setup

To begin, define your environments in GitHub to manage deployment targets. Navigate to your repository settings at GitHub Environments and create dev, staging, and prod environments. Each can have protection rules, such as requiring reviewer approval for prod deployments, enhancing security.

Environment Purpose Protection Rules Example
dev Development and feature testing No restrictions
staging Pre-production testing Require 1 reviewer
prod Live production environment Require 2 reviewers, wait timer

These environments help control access to secrets and variables, ensuring only authorized jobs can deploy to specific targets.

Database Setup with Postgres

For database management, use Postgres with separate databases for each environment to ensure isolation. You can run a single Postgres container using Docker, creating multiple databases within it, or use separate containers for added isolation. For simplicity, opt for a single container:

  • Install and run Postgres via Docker: docker run -d --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=secret -p 5432:5432 postgres:14.
  • Create databases and users:
    CREATE USER dev_user WITH PASSWORD 'dev_pass';
    CREATE DATABASE your-app-dev;
    GRANT ALL PRIVILEGES ON DATABASE your-app-dev TO dev_user;
    Repeat for staging_user/your-app-staging and prod_user/your-app-prod.

This setup, detailed in Building a Multi-Env CI/CD Pipeline, ensures each environment has its own data space, reducing the risk of cross-environment interference.

Prisma Configuration for Multi-Environment Support

Prisma, as your ORM, needs to connect to the correct database for each environment. Configure your schema.prisma file to use an environmental variable for the database URL:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id    Int    @id @default(autoincrement())
  name  String
  email String @unique
}

This approach, supported by Prisma Documentation on Environment Variables, allows dynamic database connections. For each environment, set DATABASE_URL to point to the respective database, e.g., postgresql://dev_user:dev_pass@postgres:5432/your-app-dev.

Docker and Docker Compose for Application Containerization

Containerize your application using Docker to ensure consistency across environments. Create a Dockerfile that includes your application code and runs Prisma migrations on startup:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["sh", "-c", "npx prisma migrate deploy && npm run start"]

This ensures migrations are applied before the application starts, as recommended in CI/CD with GitHub Actions for Prisma and PostgreSQL.

Use Docker Compose to manage services for each environment. Example docker-compose.yml:

services:
  postgres:
    image: postgres:14
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
    volumes:
      - postgres_data:/var/lib/postgresql/data

  app-dev:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://dev_user:dev_pass@postgres:5432/your-app-dev
    depends_on:
      - postgres

  app-staging:
    build: .
    ports:
      - "3001:3000"
    environment:
      - DATABASE_URL=postgresql://staging_user:staging_pass@postgres:5432/your-app-staging
    depends_on:
      - postgres

  app-prod:
    build: .
    ports:
      - "3002:3000"
    environment:
      - DATABASE_URL=postgresql://prod_user:prod_pass@postgres:5432/your-app-prod
    depends_on:
      - postgres

volumes:
  postgres_data:

This configuration, inspired by How to Build a CI/CD Pipeline with GitHub Actions and Docker, allows you to run specific services (app-dev, app-staging, app-prod) for each environment.

GitHub Actions Workflow for CI/CD

Set up a GitHub Actions workflow to automate building, testing, and deploying. Create a file like .github/workflows/merge-request-workflow.yml:

name: CI/CD Pipeline

on:
  push:
    branches:
      - dev
      - staging
      - master

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          context: .
          push: true
          tags: your-docker-username/your-app:${{ github.sha }}

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    env:
      IMAGE_NAME: your-docker-username/your-app:${{ github.sha }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Determine environment
        run: |
          if [ "${{ github.ref }}" == "refs/heads/dev" ]; then
            echo "ENVIRONMENT=dev" >> $GITHUB_ENV
            echo "SERVICE=app-dev" >> $GITHUB_ENV
            echo "PORT=3000" >> $GITHUB_ENV
          elif [ "${{ github.ref }}" == "refs/heads/staging" ]; then
            echo "ENVIRONMENT=staging" >> $GITHUB_ENV
            echo "SERVICE=app-staging" >> $GITHUB_ENV
            echo "PORT=3001" >> $GITHUB_ENV
          elif [ "${{ github.ref }}" == "refs/heads/master" ]; then
            echo "ENVIRONMENT=prod" >> $GITHUB_ENV
            echo "SERVICE=app-prod" >> $GITHUB_ENV
            echo "PORT=3002" >> $GITHUB_ENV
          fi

      - name: Deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            docker-compose down
            docker-compose pull ${{ env.SERVICE }}
            docker-compose up -d ${{ env.SERVICE }}

This workflow, detailed in Setup CI/CD on GitHub Actions for Multiple Environments Deployment, builds and pushes the Docker image, then deploys to the corresponding environment based on the branch, using SSH to manage the deployment on your server.

Managing Environmental Variables and Secrets

Each environment requires specific configurations, especially for database connections. Set DATABASE_URL in your docker-compose.yml for each service, or use GitHub Actions secrets for added security. For example, set secrets like DATABASE_URL_DEV, DATABASE_URL_STAGING, and DATABASE_URL_PROD in your repository settings under "Secrets and variables" for each environment, as described in Managing environments for deployment - GitHub Docs.

Environment Secret Example Usage in Workflow
dev DATABASE_URL_DEV Passed as env var during deployment
staging DATABASE_URL_STAGING Passed as env var during deployment
prod DATABASE_URL_PROD Passed as env var during deployment

Avoid hardcoding credentials in your repository; use secrets managers like AWS Secrets Manager for production, as noted in Security considerations for GitHub Actions.

Handling Database Migrations with Prisma

Prisma migrations ensure your database schema matches your application code. In the Dockerfile, include a command to run migrations on startup (npx prisma migrate deploy && npm run start), ensuring migrations are applied before the application starts. For production, consider running migrations separately before deployment to avoid downtime, as suggested in Prisma Deployment Documentation.

Alternatively, add a step in your workflow to run migrations inside the container, e.g., docker exec -it your-app-container npx prisma migrate deploy, ensuring the database is ready before the application starts.

Testing and Validation

Test your setup by pushing changes to each branch and verifying deployments. Use tools like Postman or curl to test endpoints in each environment, ensuring the correct database is used. Monitor logs and performance, setting up alerts for any issues, as outlined in How to Setup a CI/CD Pipeline with GitHub Actions and AWS.

Branch Strategy and Best Practices

Adopt a branch strategy where developers work on feature branches, merge into dev, then promote to staging for testing, and finally to master for prod. This ensures changes are tested at each stage, reducing risks. Use GitHub Actions to automate testing on every push, deploying only after successful builds, as detailed in Build a CI/CD workflow with Github Actions.

For security, ensure sensitive data is managed via secrets, and consider using managed databases for prod to enhance scalability and reliability. Monitor and log each environment to track performance and errors, ensuring a smooth transition from dev to prod.

Conclusion

This setup provides a robust framework for managing dev, staging, and prod environments with GitHub Actions, Docker, Postgres, and Prisma. It ensures isolation, security, and ease of deployment, aligning with your goal of a streamlined CI/CD pipeline. Adjust based on your specific needs, such as using managed services for production, to enhance scalability and security.


Key Citations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment