Development Infrastructure¶
The development environment follows the production deployment invariants: immutable application artifacts, no database changes during application startup, deployment through AWS Systems Manager, health-gated rollout, and automatic application rollback. Development uses shorter backup retention and host-local Redis to keep costs proportional to the environment.
docker-compose.dev.yml is the canonical EC2 development stack. Use docker-compose.local.yml for workstation development.
Runtime layout¶
- Traefik terminates TLS for
api.dev.dichit.moneyand exposes its authenticated dashboard athttps://traefik.dev.dichit.money/dashboard/. Port 8080 is not published. - Traefik runs as root inside a hardened container because it needs read-only Docker socket access for service discovery and must read/write root-owned
0600TLS and dashboard secret files prepared by the deployment script. - The application and migration images are built separately from
Dockerfile.dev. - The application image contains compiled runtime files and production dependencies only. It does not contain environment files, source, Git metadata, tests, Prisma migrations, or development dependencies.
- The migration image contains Prisma and migration/seed sources, but it never runs as part of application startup.
- Redis runs on an internal Docker network and is not exposed on an EC2 host port.
- Redis persists to the
dichit-dev-redisDocker volume. The deployment script detects the Redis runtime user, repairs the volume ownership, and exports that user into Compose before Redis starts. After Redis is healthy, deployment forces a synchronousSAVEso RDB permission or disk failures fail before migrations and app rollout continue. - App, Traefik, and Redis use Docker's local logging driver with bounded rotation: five files of up to 20 MB per container.
AWS prerequisites¶
The development EC2 instance profile needs:
AmazonSSMManagedInstanceCore;- the S3 and Secrets Manager reader policy created by
infra/aws/dev/deployment.yml.
Install Docker Engine, Docker Compose v2, AWS CLI, and SSM Agent on the instance. Authenticate Docker to GHCR with a read-only package credential before the first deployment.
Create one Secrets Manager secret containing the complete .env.dev payload as plaintext dotenv content. Required container-network values include:
NODE_ENV=development
PORT=3500
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=replace-with-a-random-development-secret
Do not store the dotenv payload as a JSON object; the deployment script writes the secret value directly to .env.dev with mode 0600.
Create a separate Secrets Manager secret containing an htpasswd users file for the dashboard. Generate a bcrypt entry interactively so the plaintext password does not enter Git or the Compose environment:
umask 077
dashboard_auth_file="$(mktemp)"
htpasswd -cB "$dashboard_auth_file" devadmin
aws secretsmanager create-secret \
--region ap-south-1 \
--name /dichit/development/traefik-dashboard-auth \
--secret-string "file://${dashboard_auth_file}"
rm -f "$dashboard_auth_file"
The secret may contain multiple username:bcrypt-hash lines. The deployment writes it to traefik/dashboard-users with mode 0600 and mounts it read-only into Traefik. Rotate it by updating the secret value and redeploying development.
1. Create the deployment path¶
Reuse the account-level GitHub Actions OIDC provider if production already created it.
aws cloudformation deploy \
--region ap-south-1 \
--stack-name dichit-development-deployment \
--template-file infra/aws/dev/deployment.yml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
GitHubOidcProviderArn=YOUR_GITHUB_OIDC_PROVIDER_ARN \
DevelopmentInstanceId=YOUR_DEVELOPMENT_INSTANCE_ID \
DockerHostRoleName=YOUR_DEVELOPMENT_INSTANCE_ROLE \
AppEnvironmentSecretArn=YOUR_DEVELOPMENT_ENV_SECRET_ARN \
DashboardAuthSecretArn=YOUR_DASHBOARD_AUTH_SECRET_ARN
Configure the GitHub development environment:
| Kind | Name | Value |
|---|---|---|
| Secret | AWS_DEV_DEPLOY_ROLE_ARN | GitHubDeploymentRoleArn stack output |
| Variable | AWS_REGION | ap-south-1 |
| Variable | DEV_DEPLOYMENT_BUCKET | DeploymentBucketName stack output |
| Variable | DEV_INSTANCE_ID | Development EC2 instance ID |
| Variable | DEV_APP_ENV_SECRET_ID | Development dotenv secret ARN |
| Variable | DEV_TRAEFIK_IMAGE | Immutable Traefik image digest |
| Variable | DEV_REDIS_IMAGE | Immutable Redis image digest |
| Variable | DEV_TRAEFIK_DASHBOARD_AUTH_SECRET_ID | Dashboard htpasswd secret ARN |
| Variable | DEV_SEED_ON_DEPLOY | true to seed on deploy; defaults to false |
| Variable | DEV_SEED_DEEP_ON_DEPLOY | true to pass --deep; defaults to false |
Image variables must use registry digests, for example:
Configure environment reviewers if development deployments require manual approval. SSH deployment secrets are no longer used.
2. Enable development backups¶
The development backup policy retains daily recovery points for 14 days by default and intentionally does not enable Vault Lock.
aws cloudformation deploy \
--region ap-south-1 \
--stack-name dichit-development-backup \
--template-file infra/aws/dev/backup.yml \
--capabilities CAPABILITY_NAMED_IAM
Tag the development EBS volume or other supported resources that should be protected:
Docker named volumes live on the EC2 storage backing /var/lib/docker; verify that the tagged EBS backup includes them and perform a restore test before relying on it.
Deployment flow¶
A push to dev performs the following:
- Builds application and migration targets from
Dockerfile.dev. - Publishes convenience tags plus content-addressed image digests with SBOM and provenance attestations.
- Uploads the Compose file, deployment scripts, and Traefik configuration to the encrypted deployment bucket.
- Invokes the EC2 host through SSM.
- Retrieves
.env.devand the dashboard htpasswd users file from Secrets Manager atomically. - Resolves the Redis runtime user, repairs the Redis volume ownership, starts Redis and Traefik, verifies Redis persistence, applies migrations, optionally seeds the database, then recreates the application.
- Checks
https://api.dev.dichit.money/healthon the EC2 host and rolls the application back if it remains unhealthy.
Use the workflow's skip_deploy option to build and publish images without changing the EC2 environment. A commit containing [skip-deploy] skips the complete development release workflow.
Development deploy seeding is controlled by GitHub environment variables on the development deployment job:
DEV_SEED_ON_DEPLOY=trueruns./scripts/seed.dev.shafter migrations.DEV_SEED_ON_DEPLOY=falseskips deploy-time seeding entirely.DEV_SEED_DEEP_ON_DEPLOY=truepasses--deepto the seeder when seeding is enabled.DEV_SEED_DEEP_ON_DEPLOY=falseruns the normal development seed.
The workflow forwards those values to /opt/dichit-dev/scripts/deploy.dev.sh, so direct host runs can still override SEED_ON_DEPLOY and SEED_DEEP_ON_DEPLOY manually if needed.
Database and seed operations¶
Application startup never migrates, resets, or seeds the database.
Run a normal development seed on the host through an approved SSM session:
Pass --deep when the larger development dataset is required:
The Reset Development Database GitHub workflow requires the exact confirmation value yes. It runs the reset inside the migration image through SSM and can optionally restart the application. It does not require SSH or Prisma tooling in the application container.
Verification after rollout¶
aws ssm describe-instance-information \
--filters Key=InstanceIds,Values=YOUR_DEVELOPMENT_INSTANCE_ID
Start an approved SSM session and inspect recent container logs with:
cd /opt/dichit-dev
source .deployment-images.env
docker compose -f docker-compose.dev.yml --env-file .env.dev logs --tail 200 app traefik redis
Verify that anonymous dashboard requests are rejected and authenticated requests succeed:
curl --head https://traefik.dev.dichit.money/dashboard/
curl --user devadmin https://traefik.dev.dichit.money/dashboard/
The first request must return 401 Unauthorized. The second prompts for the password and must return the dashboard response over HTTPS.
Also verify the public health endpoint, Redis persistence after container recreation, an application rollback, and an EBS restore at least once before treating the environment as recoverable.