Cloud Portfolio GitHub Repository Structure: How to Organise Your Projects

How you organise a repository tells a hiring manager something before they read a single line of code. A flat directory with one enormous main.tf and no folder structure signals someone who has not worked on a real team. A well-organised project with logical folders, a clear README, and a separation of concerns signals someone who thinks about maintainability. This guide covers repo structure specifically for the inside of your cloud portfolio repositories.

General structure principles

Regardless of the project type, every cloud portfolio repository should have:

  • A root-level README.md — the first thing anyone reads
  • A .gitignore appropriate for the languages and tools used
  • No secrets, credentials, or .tfstate files committed to the repository
  • A clear folder structure that groups related files — not everything at root level
  • A docs/ or diagrams/ folder for architecture diagrams and supporting documentation (optional but impressive)

One rule that applies universally: if you have to ask where a file belongs, that question is a sign the structure needs more thought. Every file should have an obvious home.

Terraform repository structure

A Terraform project for a beginner to intermediate cloud engineer:

my-cloud-infra/
├── README.md
├── .gitignore              # includes *.tfstate, *.tfstate.backup, .terraform/
├── modules/
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── compute/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── database/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
├── environments/
│   ├── dev/
│   │   ├── main.tf         # calls modules with dev-specific values
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── terraform.tfvars
│   └── prod/
│       ├── main.tf
│       ├── variables.tf
│       ├── outputs.tf
│       └── terraform.tfvars
├── .github/
│   └── workflows/
│       └── terraform.yml   # CI pipeline for plan/apply
└── docs/
    └── architecture.md

Key points in this structure:

  • Modules are reusable — the same module code runs in dev and prod with different variable values
  • The environment directories call modules, they do not duplicate infrastructure code
  • The .github/workflows/ folder holds the CI pipeline alongside the infrastructure code
  • The .gitignore explicitly excludes .terraform/ directories and state files

Kubernetes repository structure

For a multi-service Kubernetes project:

my-k8s-app/
├── README.md
├── .gitignore
├── apps/
│   ├── frontend/
│   │   ├── Dockerfile
│   │   └── src/            # application source code
│   └── api/
│       ├── Dockerfile
│       └── src/
├── k8s/
│   ├── namespace.yaml
│   ├── frontend/
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   └── hpa.yaml
│   ├── api/
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   └── configmap.yaml
│   └── ingress/
│       └── ingress.yaml
├── helm/                   # if using Helm
│   └── my-app/
│       ├── Chart.yaml
│       ├── values.yaml
│       └── templates/
├── .github/
│   └── workflows/
│       ├── build.yml       # build and push Docker images
│       └── deploy.yml      # deploy to cluster
└── docs/
    └── architecture.md

Key points:

  • Application source code and Kubernetes manifests are in separate folders — mixing them creates confusion
  • Kubernetes resources are grouped by service (frontend/, api/), not by resource type — so you can navigate to all resources for a service without jumping around
  • The Helm chart is separate from the raw manifests — both are valid deployment methods; having both demonstrates versatility
  • CI pipelines are in .github/workflows/ alongside the code they deploy

Serverless repository structure

For a serverless API project:

my-serverless-api/
├── README.md
├── .gitignore              # includes .env, node_modules/, __pycache__/
├── src/
│   ├── handlers/
│   │   ├── create.py       # or create.js, create.go
│   │   └── get.py
│   ├── models/
│   │   └── url.py
│   └── utils/
│       └── validation.py
├── tests/
│   ├── unit/
│   │   └── test_create.py
│   └── integration/
│       └── test_api.py
├── infrastructure/
│   ├── main.tf             # or template.yaml for SAM
│   ├── variables.tf
│   ├── outputs.tf
│   └── iam.tf              # IAM roles and policies in their own file
├── .github/
│   └── workflows/
│       └── ci.yml
└── docs/
    └── api-reference.md

Key points:

  • Application code is separated from infrastructure code — a common mistake is mixing Terraform and Python in the same directory
  • Tests are separated into unit tests (fast, no network) and integration tests (slower, requiring deployed infrastructure)
  • IAM resources are in their own file (iam.tf) rather than embedded in main.tf — IAM is complex enough to warrant its own file
  • No .env files committed — .gitignore covers this explicitly

README structure inside a repository

The README is the most important file in the repository. A strong README for a cloud portfolio project has these sections:

  1. Project title and one-line description — what is this and why does it exist?
  2. Architecture overview — a brief description or diagram of the components and how they connect
  3. Technologies used — the tools, services, and languages involved
  4. Architecture decisions — the most important section. Why did you choose this database? Why are the IAM permissions scoped this way? Why this deployment strategy?
  5. Prerequisites and deployment instructions — what does someone need to run this, and how?
  6. What you would change in production — honest acknowledgement of what the project lacks at portfolio scale

The architecture decisions section is what distinguishes a portfolio project from a tutorial. Do not skip it.

What always goes in .gitignore

For cloud portfolio projects, a comprehensive .gitignore should include:

# Terraform
*.tfstate
*.tfstate.backup
.terraform/
*.tfvars          # if they contain sensitive values
crash.log

# Secrets and credentials
.env
*.pem
*.key
credentials
*_credentials.json

# Language-specific
__pycache__/
node_modules/
*.pyc
.venv/

# IDE and OS
.DS_Store
.idea/
.vscode/
Thumbs.db

Verify your .gitignore is actually working — run git status after adding a test .env file to confirm it is excluded. Credentials in Git history are a permanent problem even if you delete the file later.

Common structure mistakes

  • Everything in the root directory. Ten .tf files at root level with no folder organisation signals unfamiliarity with real codebases.
  • Application code and infrastructure code mixed together. Keep application logic and deployment configuration in separate folders.
  • No separation between environments. A single Terraform file that somehow handles both dev and prod through hardcoded conditionals is harder to maintain and harder to understand.
  • Committed .terraform/ directories or .tfstate files. These should never be in the repository — check your .gitignore before the first commit.
  • A main.py or index.js at root level with no further structure. Even for a simple project, a src/ folder with logical subfolders shows better organisation than a flat file.