Overview
A reusable convention for naming cloud and Kubernetes resources consistently across a project. Adopt it as-is, or adjust the handful of project-specific choices listed in How to adapt this to your project. Everything else is generic.
Why have a naming convention
A good name tells you what a resource is at a glance — in the cloud console, in
kubectl, in Terraform state, and in the repo — without opening anything. A shared
convention makes resources predictable and searchable, lets the same string serve as the
Terraform identifier, the file path, and the runtime name, supports cost allocation and
automation through consistent tags, and avoids collisions between environments, regions,
and tenants.
How to adapt this to your project
Before using the patterns, your team makes a few decisions. The rest of the document doesn’t change.
| Decision | What to pick | Value used in this doc |
|---|---|---|
| Project code | Short identifier for the product/project, 2–4 characters | acme |
| Environments | The set of environment tokens you use | dev, stg, prod |
| Cloud + regions | Provider and the short region tokens | AWS · euc1, use1, … |
| Multi-tenant? | Whether resources are shared or dedicated per tenant | both patterns shown |
| Separator | Character between segments | - |
| Casing | Casing for all segments | lowercase kebab-case |
Throughout this document,
acmeis a stand-in project code andtenant-a/tenant-bare stand-in tenant identifiers. Replace them with your own.
Single-tenant projects: ignore the tenant segment and Patterns 2–3 entirely.
Pattern 1 alone covers everything you need.
General rules
- Lowercase letters, digits, and the separator only — no spaces, underscores, or uppercase.
- Segments always appear in the fixed order shown; never reorder them.
- Keep tokens compact. Region and resource tokens are abbreviated specifically so they
don’t contain the
-separator and confuse parsing. - Respect provider limits. Some names have hard caps and uniqueness rules (for example,
S3 bucket names are 3–63 characters and globally unique). Keep the
purposesegment short. - One name everywhere: the same string in Terraform, in the repo path, and on the running resource.
Pattern 1 — Shared resources
Resources that serve the whole project or all tenants: VPC, the cluster, shared object storage, a shared database, container registries, and so on.
<project>-<env>-<region>-<resource>[-<purpose>]
Examples (project code acme):
acme-dev-euc1-eks # Kubernetes cluster (dev) — shared across all tenants
acme-dev-euc1-vpc # VPC for the dev environment
acme-prod-euc1-rds # primary database
acme-prod-use1-rds # same kind of database in a second region
acme-dev-r53-example-com # DNS hosted zone — global, no region segment
purpose is optional. If there is only one resource of a given type in the environment
(like the cluster), you can omit it.
Pattern 2 — Tenant-specific resources (multi-tenant projects only)
Resources dedicated to a single tenant/customer: dedicated buckets, IAM roles, secrets, and the like.
<project>-<tenant>-<env>-<region>-<resource>-<purpose>
Examples:
acme-tenant-a-dev-euc1-s3-backoffice # storage bucket for tenant-a
acme-tenant-a-dev-euc1-sm-db-creds # secret holding DB credentials
acme-tenant-b-prod-use1-s3-user-uploads # uploads bucket for tenant-b
Pattern 3 — Tenant-scoped objects inside shared resources (multi-tenant only)
When you create a tenant-scoped logical object inside a shared resource (a database inside the shared DB server, a namespace inside the shared cluster, and so on), use the tenant identifier directly — no project, env, or resource prefix.
| Container resource | Tenant-scoped object | Name |
|---|---|---|
Shared cluster (acme-prod-euc1-eks) | Kubernetes namespace | tenant-a |
Shared DB server (acme-prod-euc1-rds) | Database | tenant-a |
| Shared DB server | DB user / role | tenant-a |
Examples:
# Inside acme-prod-euc1-eks
namespace: tenant-a
namespace: tenant-b
# Inside acme-prod-euc1-rds
CREATE DATABASE "tenant-a";
CREATE USER "tenant-a" WITH PASSWORD '...';
If the same tenant has multiple objects of the same kind inside one shared resource,
append a purpose segment:
tenant-a-app # main app DB
tenant-a-analytics # analytics DB for the same tenant
Segments
| Part | Meaning | Example |
|---|---|---|
project | Project/product code, 2–4 characters | acme |
tenant | Tenant identifier (Patterns 2 & 3 only) | tenant-a, tenant-b |
env | Environment | dev, stg, prod |
region | Cloud region code, short form | euc1, euw1, use1, usw2, apse1 |
resource | Resource type, 2–4 characters | s3, rds, eks, lam |
purpose | What the resource is for (kebab-case) | backoffice, user-uploads |
Region tokens
Compact region codes, with dashes removed so they don’t collide with the - separator.
The table below is for AWS; keep only the regions you actually use.
| AWS region | Token |
|---|---|
eu-central-1 (Frankfurt) | euc1 |
eu-west-1 (Ireland) | euw1 |
eu-west-2 (London) | euw2 |
eu-north-1 (Stockholm) | eun1 |
us-east-1 (N. Virginia) | use1 |
us-east-2 (Ohio) | use2 |
us-west-2 (Oregon) | usw2 |
ap-south-1 (Mumbai) | aps1 |
ap-southeast-1 (Singapore) | apse1 |
ap-southeast-2 (Sydney) | apse2 |
ap-northeast-1 (Tokyo) | apne1 |
ca-central-1 (Canada) | cac1 |
sa-east-1 (São Paulo) | sae1 |
Other clouds: apply the same idea — pick a short, dash-free token per region. For example,
usc1for GCPus-central1, orweufor AzureWest Europe.
Resource type tokens
Common abbreviations. Extend the list for your stack; keep each token 2–4 characters and unique within your naming scheme.
| Cloud (AWS) | Type | Kubernetes | Type |
|---|---|---|---|
vpc | VPC | ns | Namespace |
sg | Security group | cm | ConfigMap |
eks | Kubernetes cluster | sec | Secret |
ec2 | EC2 instance | svc | Service |
asg | Auto Scaling group | dep | Deployment |
rds | Relational database | sts | StatefulSet |
s3 | Object storage bucket | ds | DaemonSet |
ecr | Container registry | ing | Ingress |
lam | Lambda function | sa | ServiceAccount |
sm | Secrets Manager secret | hpa | HorizontalPodAutoscaler |
ssm | SSM parameter | pvc | PersistentVolumeClaim |
iam | IAM role / policy | cr | ClusterRole |
r53 | Route 53 hosted zone | crb | ClusterRoleBinding |
cf | CDN distribution | role | Role |
sqs / sns | Queue / topic | rb | RoleBinding |
ddb | NoSQL table | ||
alb / nlb | Load balancers | ||
kms | Encryption key |
Global (region-less) resources
Some services are global — they don’t live in a region. For these, omit the region segment entirely:
acme-dev-r53-example-com # DNS hosted zone
acme-prod-iam-eks-admin # IAM role
acme-prod-cf-static-assets # CDN distribution
Exceptions — keep upstream/canonical names
The convention above applies to infrastructure you build and own. For third-party tools you deploy from upstream, keep their canonical names as-is. Don’t add your project prefix. This applies to:
- Helm releases —
nginx-ingress,cert-manager,external-dns,argocd,prometheus,grafana,external-secrets, and so on. - Kubernetes operators and controllers — use their default install names.
- Open-source services deployed into your infra — keep the upstream project name.
- System namespaces —
kube-system,ingress-nginx,cert-manager(you don’t rename these anyway).
Kubernetes resources (ConfigMaps, Secrets, etc.)
In-namespace Kubernetes objects you manage follow the same convention as cloud
resources. This keeps Terraform file names, repo structure, and actual cluster resources
aligned — eks-resources/config-maps/acme-dev-euc1-cm-backoffice.tf produces a ConfigMap
named acme-dev-euc1-cm-backoffice. One name everywhere — in the repo and in kubectl.
Shared (Pattern 1):
<project>-<env>-<region>-<resource>-<purpose>
Tenant-specific (Pattern 2):
<project>-<tenant>-<env>-<region>-<resource>-<purpose>
Examples:
# Shared — in kube-system, monitoring, or any shared namespace
acme-dev-euc1-cm-backoffice # ConfigMap
acme-prod-euc1-cm-fluent-bit-config # shared logging config
acme-prod-euc1-sec-pull-secret # shared image-pull secret
acme-prod-euc1-cr-platform-admin # ClusterRole
# Tenant-specific — inside namespace tenant-a
acme-tenant-a-prod-euc1-cm-backoffice # ConfigMap
acme-tenant-a-prod-euc1-sec-db-creds # Secret with DB credentials
acme-tenant-a-prod-euc1-svc-backoffice # Service
acme-tenant-a-prod-euc1-dep-backoffice # Deployment
acme-tenant-a-prod-euc1-ing-backoffice # Ingress
acme-tenant-a-prod-euc1-sa-backoffice # ServiceAccount
acme-tenant-a-prod-euc1-hpa-backoffice # HorizontalPodAutoscaler
Tags
The name is for humans browsing the cloud console. Tags are for cost allocation, filtering, and automation. Standardize one tag set and fill the values per resource.
# Shared
tags = {
company = "<company>"
project = "<project>"
environment = "<env>"
region = "<region>"
scope = "shared"
terraform = "true"
}
# Tenant-specific
tags = {
company = "<company>"
project = "<project>"
environment = "<env>"
region = "<region>"
scope = "tenant"
tenant = "<tenant>"
terraform = "true"
}