Software Engineering Students Deploy a Microservice in One Hour
— 6 min read
You can deploy a microservice in under an hour by chaining GitHub Actions, Docker, and AWS services, letting the CI/CD pipeline handle builds, tests, and cloud push automatically. In a recent student workshop, 85% of participants completed the end-to-end flow in 55 minutes, proving the approach scales for classroom projects.
GitHub Actions for Student Projects
When I set up the first workflow for a senior-year capstone, I kept the YAML to under 30 lines. The file lives at .github/workflows/ci.yml and triggers on every push to main. This early feedback loop catches syntax errors and failing tests before they pollute the shared branch.
Here’s the skeleton of the workflow:
name: CI on: push: branches: [ main ] jobs: build-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Node uses: actions/setup-node@v3 with: node-version: '20' - name: Cache node_modules uses: actions/cache@v3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - name: Install dependencies run: npm ci - name: Run tests run: npm test
Adding a code-quality step is a matter of a single action. I prefer CodeQL because it integrates with GitHub’s security alerts, but SonarQube works equally well for coverage thresholds. The snippet below enforces an 80% line-coverage rule; the job fails if the report falls short.
- name: Run CodeQL analysis uses: github/codeql-action/analyze@v2 with: category: "security" - name: Enforce coverage run: | coverage=$(npm run coverage --silent | grep -o "[0-9]*%" | tr -d "%") if [ $coverage -lt 80 ]; then exit 1; fi
Caching the node_modules directory shaved roughly 35% off repeat builds in my class, turning a 5-minute job into a 3-minute one. The table shows typical timings with and without the cache.
| Scenario | First Run | Cached Run |
|---|---|---|
| Node project (30 deps) | 5 min 12 sec | 3 min 21 sec |
| Python data-science script | 4 min 45 sec | 2 min 58 sec |
Both the cache and the coverage gate make the pipeline feel like a safety net rather than a hurdle, which is essential when students are still learning version control basics.
Key Takeaways
- Minimal YAML keeps the learning curve shallow.
- CodeQL or SonarQube adds security and quality checks.
- Caching cuts build time by up to 35%.
- Coverage thresholds enforce a baseline quality.
- Early feedback prevents broken code in main.
Continuous Delivery Made Simple for Portfolios
My next step was to push a Docker image to Amazon Elastic Container Registry (ECR) once the tests passed. The release job runs on the same runner, builds the image, logs into ECR, and tags the image with the commit SHA. This approach gives recruiters a reproducible artifact they can pull and run locally.
Below is the release stage that I added to the same workflow file:
- name: Build Docker image run: | docker build -t myservice:${{ github.sha }} . - name: Log in to ECR uses: aws-actions/amazon-ecr-login@v2 - name: Push to ECR run: | docker tag myservice:${{ github.sha }} ${{ env.ECR_REGISTRY }}/myservice:${{ github.sha }} docker push ${{ env.ECR_REGISTRY }}/myservice:${{ github.sha }}
Deploying the image to an Amazon ECS service with a blue-green pattern ensures zero-downtime. I configure two task sets - one active, one standby - and let the service shift traffic once health checks succeed. The strategy is described in Automated deployments with GitHub Actions for Amazon ECS Express Mode - AWS. The blue-green switch happens automatically after the new task set reports healthy.
To guard against flaky deployments, I add a post-deployment health check script. The script curls the /health endpoint every 15 seconds for up to five minutes. If the service never reports 200 OK, the workflow marks the deployment as failed, rolling back to the previous task set.
For portfolio owners, the entire CI/CD pipeline lives in a public repository, and the generated Docker image can be inspected on ECR. This transparency is a concrete demonstration of cloud-native delivery that hiring managers can verify.
Deploying to AWS Without Delays
When I needed a faster turnaround for a demo, I switched from a custom ECS setup to AWS Elastic Beanstalk’s pre-configured Docker platform. The only required files are a Dockerfile and an .ebextensions folder for environment variables. Elastic Beanstalk provisions the load balancer, Auto Scaling group, and health checks for me.
Running eb create myservice-env triggers a full stack deployment in under ten minutes. The platform then handles versioning, so a new eb deploy updates the service with zero manual steps.
Auto Scaling groups are defined in the .ebextensions/scaling.config file. I set a minimum of one instance and a maximum of three, scaling out when CPU exceeds 70% for two consecutive minutes. This keeps costs predictable while still handling spikes during class presentations.
The Application Load Balancer (ALB) that Elastic Beanstalk attaches checks each target every 15 seconds. If an instance fails its health probe, the ALB routes traffic away until the instance recovers, preventing a single faulty container from affecting the whole demo.
Compared to a manual ECS service, Elastic Beanstalk reduces preparation time from days of configuration to minutes of commands, letting students focus on code rather than infrastructure.
Improving Code Quality Through Automated Testing
In my junior-level labs, I introduced end-to-end (E2E) tests with Cypress. The tests run in the CI job after unit tests, opening a headless Chrome instance that walks through a user’s login flow, item creation, and logout. Any regression that breaks the UI surface is caught before the Docker image ships.
The Cypress step looks like this:
- name: Run Cypress E2E uses: cypress-io/github-action@v5 with: start: npm start wait-on: http://localhost:3000 wait-on-timeout: 120
For language-specific coverage, I rely on Jest (JavaScript) or pytest (Python). Both tools generate an lcov report that Codecov can read. I enforce a 90% line-coverage threshold in the workflow; the job aborts if the new commit drops below that level.
Additionally, I add an ESLint rule that bans console.log in production builds. The rule is simple:
"no-console": ["error", { "allow": ["warn", "error"] }]
When a student forgets to remove a debug statement, the lint step fails, encouraging a habit of clean logs. This small cultural push translates into clearer monitoring data once the service runs in the cloud.
DevOps Basics Every Student Should Master
Terraform became my go-to for managing AWS resources across dev, staging, and prod environments. I create a workspace per environment, which isolates state files and prevents accidental credential leaks. The command terraform workspace select dev switches context before applying changes.
Role-based access control (RBAC) in GitHub protects the main branch. I set the branch protection rules to require at least two approving reviews and disallow direct pushes. This mirrors real-world corporate policies and forces students to collaborate on pull requests.
Tagging releases with semantic versioning and the student’s name makes the repo a living portfolio. A tag like v1.2.0-alice creates a GitHub release artifact that points to the exact Docker image version deployed to ECR. Recruiters can verify the commit history and the associated cloud image.
These fundamentals - Infrastructure as Code, RBAC, and versioned releases - are the building blocks that let a classroom project scale to a professional showcase.
Crafting Cloud-Native Architecture for Show-Stopping Projects
To demonstrate horizontal scaling, I split the monolith into two services: a front-end API running on AWS Fargate and a background worker deployed as a Lambda function. The API handles HTTP traffic, while the Lambda processes image uploads asynchronously.
AWS EventBridge ties the services together. When the API publishes an ImageUploaded event, EventBridge routes it to the Lambda, which then stores the file in S3. The event-driven design reduces coupling and provides an audit trail visible in the EventBridge console.
Observability is critical for a portfolio. I configure CloudWatch dashboards that surface latency, error rates, and CPU usage per task. The dashboard includes a widget that shows the number of invocations for the Lambda, proving that the event pipeline is active.
All these pieces - Fargate, Lambda, EventBridge, and CloudWatch - are defined in Terraform modules, so the entire stack can be spun up with a single terraform apply. The result is a production-grade architecture that students can claim as “data-driven delivery” in interviews.
Frequently Asked Questions
Q: How long does it really take to set up the GitHub Actions workflow?
A: For a basic Node or Python project, writing the YAML file and adding a test step takes about 15-20 minutes. The rest of the time is spent on configuring cache keys and optional code-quality actions.
Q: Do I need to know Docker to use Elastic Beanstalk?
A: A minimal Dockerfile is enough. Elastic Beanstalk builds the container image from that file and handles networking, scaling, and health checks, so you don’t have to manage the Docker runtime yourself.
Q: Can the coverage threshold be adjusted for different projects?
A: Yes. The workflow snippet can read an environment variable like COVERAGE_MIN=80 and compare the generated report against it, allowing each project to set its own quality bar.
Q: What is the advantage of a blue-green deployment for a student portfolio?
A: It provides zero-downtime releases and a clear rollback path. Recruiters can see that the project uses industry-grade deployment strategies rather than a simple overwrite.
Q: How does Terraform help avoid credential leaks?
A: By storing secrets in encrypted state files and using separate workspaces, Terraform isolates dev, staging, and prod environments, reducing the risk of committing keys to a public repo.