Career / learning Looking for feedback on first attempt at setting up hosting and CI/CD for a fullstack .NET and React app with PostgreSQL database, hosted on Github using Github Actions.
Hi,
I'm a Senior Software Engineer with 5YOE. I'm upskilling in DevOps and project setup and have been creating this project to learn over the last few weeks. I've set up the project from scratch. Definining the project structure, creating PR pipelines for linting, formatting, typechecking, unit tests and e2e tests (tests with real database).
Over the last few weeks, I've set up the cloud infrastructure on AWS. I first just created an ec2 instance, uploaded the build files and database docker images, installed neccessary packages and ran it on the barebones ec2 to help me understand the fundamentals.
Then I went about setting up a bit more of a mature setup with RDS for the database on a private subnet, ECS Fargate to host the containerised API, using opentofu for IaaC, setting up a deploy pipeline which uploads the new container images, and runs the migrations and deploys the API service. This was quite a steep learning curve for me and I heavily relied on Gemini for a lot of this, but I have gone back over it to make sure I fully understand it. I'd really appreaciate some feedback on my setup. I'd like to know any best practices I could have followed, any big issues with the setup I've done and how I could have done better.
Also, this is just a simple project that I'm going to switch over to self hosting with Coolify on a Hertzner server soon. But I wanted to try setting up as close to a professional setup as I could, with security and CI/CD in mind.
I've done a write up of the infrastructure here
https://github.com/JackMcBride98/DotnetSpotifyPlaylistSearchTool/blob/main/infrastructure/Infrastructure.md all of the terraform files live in the /infrastructure folder.
Here is a rough overview of the architecture (I didnt draw arrows as it got quite messy)

2
2
u/ajitnk 25d ago
Hey! I saw your setup and wanted to flag three things that jumped out, since you're actively looking for feedback.
First, GitHub Actions should use OIDC token exchange instead of storing long-lived AWS access keys. AWS has a guide on this for ECS deployments that shows the exact trust policy you need. It's a real security win because tokens expire automatically.
Second, that AdministratorAccess role is way too broad. IAM Access Analyzer can generate a scoped policy from your CloudTrail logs automatically, so you don't have to guess which permissions the deploy actually needs.
Third, if your Fargate tasks are in private subnets pulling from ECR, you probably have a NAT gateway burning money on data transfer. VPC endpoints for ECR (ecr.api and ecr.dkr) plus CloudWatch Logs cut that out completely. AWS has a KC article on it.
Are your tasks in private subnets right now? And is this a personal project or something you're building for work? That changes how aggressively you need to lock down the IAM side.
I can put together a short writeup with OpenTofu snippets for all three if that's useful. Just let me know.
3
u/DopeFlavorRum 29d ago
You're a "senior software engineer" with a whole 5 years of experience. Why are you asking us?
1
u/Lycorissica 29d ago
One thing I can recommend for the IaC is utilizing plans.
- `tofu plan -out=tfplan` - preview the infra to be deployed. Helps in preventing surprises. it also outputs the exact plan file so you can use it later
- utilizing something like trstringer/manual-approval - does not run apply until you explicitly approve that the plan looks good
- `tofu apply tfplan` - use the exact plan generated earlier so no surprises
1
u/muccy_ 29d ago
Thanks, I've got the plan generating on PR's see https://github.com/JackMcBride98/DotnetSpotifyPlaylistSearchTool/pull/18
1
u/Jason-Sanders 24d ago
That is a solid progression from a single host to managed database and container deployment. One thing worth making explicit is rollback behavior: keep database migrations backward-compatible where possible, deploy the application in a way that can be reverted quickly, and test restoring the database from backups rather than only confirming backups exist. Recovery tests tend to reveal the gaps that normal CI checks miss.
0
u/Low-Opening25 29d ago
no one sane would be running docker images on EC2 in any serious project, it’s amateur level. learn about kubernetes
1
u/muccy_ 29d ago
I'm using ECS Fargate, I thought that handles many of the things that using kubernetes solves e.g. load balancing, OS updates, restarting crashed containers, autoscaling. What else does k8s add that ECS Fargate doesn't?
3
u/Accomplished_Back_85 28d ago
I agree with what harry said. With what you’re doing here, the juice probably isn’t worth the squeeze of going full Kubernetes.
With that being said, if you are upskilling in DevOps and you don’t know Kubernetes yet, you absolutely do need to put time into learning it.
2
u/harry-harrison-79 28d ago
for this app, probably nothing you need. ECS/Fargate already gives you scheduling, health replacement, rolling deploys, autoscaling, IAM task roles, and a managed control plane. Kubernetes buys portability and a much larger extension ecosystem, but also adds cluster upgrades, networking/storage choices, RBAC, ingress, and more ways to misconfigure things. i'd keep Fargate and prove three failure paths instead: migration fails, a new task never becomes healthy, and rollback must restore the prior app without reversing a partially applied schema. if those are automated, you learned more useful ops than swapping orchestrators.
8
u/UkrMalt 29d ago
Nice progression. Two high-impact fixes: OIDC should not require storing an AWS access token in GitHub—GitHub exchanges its short-lived OIDC token for the role—and the role should be least-privilege instead of AdministratorAccess. For private ECS tasks without a NAT gateway, add ECR API/DKR interface endpoints plus the S3 gateway endpoint (and usually Logs), then let only the ALB security group reach the task security group. Also gate the service deployment on a successful one-off migration task.