Sep 07, 2025·8 min read

Least privilege for small teams in GitHub, GitLab, cloud

Least privilege for small teams starts with GitHub, GitLab, and cloud admin groups. Cut risky access first and finish a useful cleanup in weeks.

Least privilege for small teams in GitHub, GitLab, cloud

Why small teams end up with too much access

Small teams usually hand out access for speed, not because someone made a terrible call. A founder gives admin rights so a developer can fix a late-night deploy. Someone else gets owner access because billing broke once, and nobody wants that delay again. A few months later, half the team can do almost anything.

That happens because small teams run on trust and memory. People remember who feels safe, but they do not always remember which rights they granted during a launch, a hiring rush, or an incident. Access grows one quick fix at a time.

Old accounts add another layer of risk. Contractors finish a project. Employees move to a new role. Nobody removes the old permissions because nothing looks urgent. Those accounts sit there until a mistake, a stolen laptop, or a reused password turns them into a real problem.

Shared owner accounts are even worse. One login with broad power can bypass the controls you think you have in GitHub, GitLab, or a cloud console. If several people know that password, your audit trail is already broken. When something changes, you cannot say who did it with confidence.

Most teams also think the problem is messier than it is. Usually, a few broad groups create most of the risk. The people who can change repository settings, delete projects, rotate secrets, manage billing, or open production systems matter far more than everyone with normal daily access.

The same pattern shows up again and again: an early admin group that never got trimmed, one or two shared high-power accounts, former staff who still have access, and emergency permissions that never expired.

That is why access reviews feel bigger than they are. The real danger usually does not come from fifty tiny exceptions. It comes from a short list of accounts and groups that can cause damage fast.

Start with the accounts that can hurt you fast

The first pass should focus on damage, not on a perfect role model. In GitHub, GitLab, and cloud consoles, a small number of accounts can delete code, lock out the team, expose secrets, or run up a large bill in one bad afternoon.

Make one short sheet and list every account with broad control. That usually means organization owners, group owners, billing admins, cloud root users, and any emergency or break-glass accounts. If you do only this step well, you already cut a lot of risk.

Look for accounts that can delete or transfer repositories, rotate secrets or deploy keys, change SSO or MFA settings, create major cloud spend, or stop and delete production systems. Those are the accounts that deserve attention first.

Teams often miss the money side. An account does not need full admin rights to cause pain if it can create expensive instances, raise service limits, or change billing settings. Treat those accounts the same way you treat someone who can shut down production.

Emergency accounts need extra care. Teams keep them for good reasons, then forget where the password lives, who can use it, or whether MFA still works. Check who can access them, when they were last tested, and whether they still need broad rights.

Bots and machine users belong in the same review. A deploy bot with old repo access, a CI user with broad registry permissions, or a script account that can read every secret is still a risky account. Treat non-human access the same way you treat staff access: give it an owner, define the exact job, and remove anything extra.

This is where the work gets practical. You do not need months of planning. You need a clear list of high-impact accounts, a named owner for each one, and a decision on what each account can stop, delete, unlock, or spend.

Map your real groups and tools

Most small teams do not manage access in one place. They have GitHub teams, GitLab groups, cloud IAM groups, a few direct admin grants, and old exceptions left behind after a project ended. If you rely on memory, you will miss the access that causes the most trouble.

Start with one plain sheet. Put every group, role, and direct grant in it, even if the names are messy. You are not fixing naming yet. You are trying to see who can do what right now.

A basic sheet needs only a few columns: the group or role name, where it lives, who belongs to it, what it allows, and why each person still needs it today.

That last column matters most. "Deploys production every week" is a clear reason. "Might need it" usually means nobody reviewed it.

This sheet exposes overlap fast. One person may sit in a GitHub admin team, a GitLab maintainer group, and a cloud group with production access. Different names can grant almost the same power. Once you line them up side by side, duplicate access stops looking normal.

Do not get stuck on labels. If one group is called "eng-core" and another is called "platform-admin-old," leave the cleanup for later. Write what each one actually does. Real access matters more than tidy names.

Keep the scope practical. Focus on people, not org charts. A contractor who finished two months ago, a designer with repo admin, or a founder who still has full cloud console access after handing off operations tells you more than a polished permission model.

By the end of this step, you should have one working map. It does not need to look good. It needs to make risky access obvious enough that you can start cutting it.

A 3-week cleanup plan

A small team does not need a six-month access project. For most teams, three focused weeks is enough to cut the worst risk, keep work moving, and build a cleaner baseline.

The trick is to stop making the mess bigger while you clean it up. If people keep handing out admin access during the review, you will spend all your time chasing changes.

In week 1, freeze new admin grants unless a manager approves them. Then remove access for people who should not be there anymore: former staff, contractors who finished months ago, and bot accounts nobody can explain. This part is usually fast because the bad cases are easy to spot.

In week 2, replace broad access with simple job-based groups. Look at what people actually do each day, then move them into groups like engineering, deployers, finance, or support. A developer rarely needs owner rights in GitHub or GitLab, and most people do not need full cloud console access. If one person wears two hats, give them two narrow groups instead of one giant admin role.

In week 3, test real work and write it down. Ask a few people to do normal tasks: merge code, view logs, restart a service, check billing, or update secrets if that is part of their job. Fix breakage the same day. When the groups hold up in real use, document the final list, who approves changes, and which roles stay rare.

Keep the group list short. If you end week 3 with fifteen slightly different roles for a ten-person team, you probably rebuilt the same mess with nicer names.

One simple rule helps after the cleanup: no direct admin grants unless there is a short reason, an owner, and an end date. That habit keeps access from drifting out of control again.

GitHub and GitLab roles to cut first

Make Reviews Easier Each Week
Oleg can set up a weekly review process your team will actually follow.

Most small teams do not need many people who can change settings, add members, or recover the account. Those roles create the biggest problems when one laptop gets stolen or one person leaves on bad terms.

Start with ownership. In GitHub, keep Organization Owner to the two or three people who handle billing, account recovery, SSO, and security settings. In GitLab, do the same with Group Owner. If six people can recover the account, six people can lock everyone else out.

Then look at repo and project admin. Many developers ask for admin because it removes friction, not because they need it every day. Most of the time they only need write access to push code, open pull requests, merge, and work with issues. Keep GitHub Admin or GitLab Maintainer for the people who actually manage branch protection, runners, integrations, and project settings.

Contractors need tighter boundaries. Give them access to one repo, one project, or one group with a clear end date. Do not drop them into the whole organization because they might need another repo later. If they do need more, add it then.

A good first pass is simple: keep owner roles with a tiny recovery team, move developers down to write or developer access unless they manage settings, give contractors access to one project at a time, and remove dormant users before you argue about edge cases.

After every role change, check the credentials that sit outside the role list. Teams often miss the real risk here. A person may lose admin access and still keep a personal access token with broad scopes, an old deploy key, or CI variables they created months ago.

Review personal access tokens and their scopes, deploy keys and machine users tied to old repos, CI variables that hold production secrets, and any app integrations, webhooks, or runners added by former admins.

Protected branches and deployment paths matter more than people expect. If someone can change branch rules, edit release workflows, or swap a secret in CI, they can still do real damage without full owner access. Settings and secrets matter at least as much as the member list.

If a developer says they need admin, ask what task requires it this week. In many teams, the honest answer is "almost nothing." That question cuts a lot of access fast.

Cloud console permissions to cut first

Cloud consoles get messy fast because full admin access feels easier than thinking through each job. On a small team, that shortcut gives too many people the power to change billing, break production, or expose secrets in one click.

Start by splitting access into three buckets: billing, production operations, and day-to-day developer work. Most people should sit in one bucket, not all three.

Billing access should stay with the founder, finance, or one trusted operator. Engineers rarely need permission to change payment methods, buy reserved capacity, or open spending limits.

Production access needs a smaller group than most teams expect. If someone is not on call and does not handle live incidents, they probably do not need write access to production resources.

Developer access should focus on the services they build and test. Give them rights in a dev or staging project, then add limited production access only if their daily work truly needs it.

A few cuts usually matter first. Remove full admin roles from shared groups. Take billing admin away from engineering groups. Replace write access to logs and metrics with read-only roles. Limit secret management and IAM changes to a tiny set of people. Keep production delete permissions with the smallest practical group.

Shared groups are often the biggest risk. One old group like "engineering" or "devops" can quietly carry owner or editor rights for years. New hires, contractors, and people who changed roles all inherit too much without anyone noticing.

Read-only access solves more than teams think. Many people only need to inspect logs, metrics, alerts, and cost reports to answer questions or debug an issue. They do not need permission to restart instances, rotate secrets, or edit network rules.

Emergency access should live in one controlled account, not spread across personal accounts "just in case." Protect it with strong MFA, store the recovery process where a small trusted group can reach it, and test it on a schedule. If nobody tests it, it is not emergency access. It is a guess.

Cut the permissions that cause the most damage first, and the rest of the cleanup gets much easier.

A simple example from a 10-person team

Review Risky Access First
Get a short outside review of owner, admin, and billing accounts before they cause trouble.

A 10-person product team does not need a perfect access model. It needs a sane one.

The founder keeps owner access in GitHub, GitLab, and the main cloud account. That is broad, but one person has to hold the controls for billing, legal ownership, emergency recovery, and account changes. The real mistake is giving that same level to three or four other people because it feels convenient.

Two engineers still have admin rights because they handle releases, fix broken pipelines, and unblock deployments at odd hours. The team does not pretend otherwise. It only cuts what they do not need, such as billing access, organization settings outside release work, and extra cloud privileges that have nothing to do with shipping code.

A freelance infrastructure contractor gets production access for one month during a migration. The team writes down the end date on day one, limits access to the systems involved in that project, and removes it when the work ends. Temporary access only works if someone owns the cleanup.

Everyone else moves into narrow groups. Developers get access only to the repos they work in. QA can view pipelines and test environments but cannot change org settings. Support can read logs and dashboards but cannot deploy. Designers and product staff stay out of the cloud console.

That setup is usually enough to cut risk fast without slowing the team down.

Mistakes that slow the cleanup down

Teams usually know they have too much access. They still delay the fix because they wait for a perfect permission model. That wait keeps the risky access in place. If two people still have owner rights they do not need, the problem is not the missing spreadsheet. The problem is the rights.

Another common mistake is copying an enterprise IAM design into a very small team. A six-person company does not need fifty roles, three approval layers, and a naming scheme nobody remembers. People stop using the model, then ask for broad access again because the simple path is gone.

Small teams do better when they trim the obvious risks first. Start with the accounts that can delete repos, rotate secrets, change billing, or bypass branch protection. Sort out the finer details later.

Service accounts also cause trouble when nobody documents them. A bot token created a year ago can still have owner-level access in GitHub, GitLab, or a cloud console, and no one knows what depends on it. That is how teams think they removed admin rights while a hidden back door still exists.

A few habits keep the cleanup moving. Reduce broad access in small steps, not in one giant redesign. Write down every service account, what it does, and who owns it. Keep roles simple enough that the team can explain them out loud. Test one change, then confirm builds, deploys, and alerts still work.

The fastest way to lose support is to cut access with no rollback plan. If a deploy fails and nobody can fix it, people will push to restore the old admin rights right away. Keep a short fallback plan: who can grant temporary access, how long it lasts, and how you remove it after the task is done.

That approach is less elegant than a full IAM redesign. It also gets risky access out of the system sooner, which is the point.

A 15-minute weekly checklist

Cut Admin Sprawl Now
Oleg helps small teams trim GitHub, GitLab, and cloud access without slowing releases.

A weekly access review should feel boring. That is a good sign. If it takes 15 minutes and happens every week, you catch most access creep before it turns into a real problem.

Keep it narrow. You are not redesigning every role. You are checking the few things that can go wrong fast.

Use the same order each week. Open GitHub, GitLab, and your cloud console admin pages. Check who has owner, admin, billing, or account-level control. Remove access for anyone who left, paused work, or finished a contract. Scan personal access tokens, app tokens, and SSH keys for old dates or unknown names. Then ask one plain question for each person with broad access: do they still need this right now?

Old contractor accounts are often the easiest win. A designer who helped for two weeks may still sit in a GitHub org. A freelance engineer may still have a GitLab token that works. Someone from finance may still hold cloud billing access after a one-time task. These are small misses, but they stack up.

Set a simple rule for stale credentials. If a token or SSH key has not been reviewed in the last 30 to 60 days, inspect it. If nobody can explain what it does, remove it and watch for breakage. Most of the time, nothing happens.

Keep a tiny log in a shared doc or ticket. Write down what you removed, what you kept, and what needs follow-up. After a month or two, patterns show up fast. You will see the same teams asking for broad access, the same repos collecting old tokens, and the same cloud projects keeping too many admins.

Small, regular cuts work better than a big cleanup that never gets scheduled.

What to do next

Pick one system this week and clean that first. Trying to fix GitHub, GitLab, and your cloud console in one push usually turns into a long planning exercise, and nothing changes. Start where a bad permission can cause fast damage: org owners, repo admins, billing admins, production access, and anyone who can create tokens or change login settings.

After that, assign ownership on paper, not just in conversation. Write down one person who owns access decisions, one backup owner, and one emergency process for urgent changes. If a teammate leaves on Friday night or an account acts strangely on Sunday morning, people should know exactly who can remove access and who approves temporary access.

A simple first pass works well: list the three riskiest groups or roles in one system, check who still needs them for daily work, downgrade people who only need read, deploy, or support access, record who owns the system and who covers for them, and put the next review date on the calendar.

Then repeat the same pattern everywhere. You do not need a huge policy deck. You need a routine people can follow without debate. If your GitHub review is clear and fast, your GitLab and cloud reviews should feel almost the same.

Keep your notes plain. Write things like "Mia can approve billing changes" or "Jon can deploy to staging but not production." That makes the next review faster and cuts down on guesswork when the team grows.

If you want an outside pass, Oleg Sotnikov at oleg.is helps startups and small businesses tighten infrastructure, access, and technical operations without turning it into a long IAM project. A short review from an experienced Fractional CTO is often enough to cut the obvious risk and leave the team with a process it will actually keep using.

Frequently Asked Questions

What should I review first when a small team has too much access?

Start with the accounts that can delete code, change billing, rotate secrets, or lock your team out. In most teams, that means organization owners, group owners, billing admins, cloud root or account owners, and emergency accounts.

If you only review one thing this week, review those accounts first. They create most of the damage fast.

How many GitHub or GitLab owners should a small team keep?

Keep owner access with a very small recovery group. In many small teams, that means one founder and one or two backups.

If six people can recover the account or change login settings, six people can also cause a major problem. Fewer owners make reviews much easier.

Do most developers need admin rights in GitHub or GitLab?

Usually, no. Most developers need to push code, open pull requests or merge requests, merge changes, and work with issues. They do not need to change organization settings, branch rules, or member access every day.

Ask what task requires admin this week. If they cannot name one, move them to a lower role.

What should I do with old contractor accounts?

Remove them as soon as the work ends. Do not wait for the next cleanup.

When you add a contractor, write down the end date on day one and limit access to the exact repo, project, or system they need. That keeps temporary access from turning into permanent access.

How should we handle bots and service accounts?

Treat bots like staff accounts with a job and an owner. Give each one a clear purpose, the smallest access it needs, and someone who reviews it.

If nobody can explain what a bot does, remove its access and watch for breakage. Old machine accounts often keep more power than people realize.

Which cloud permissions should we cut first?

Cut billing admin from engineering groups, remove broad admin from shared groups, and limit secret and IAM changes to a tiny set of people. Those changes reduce both outage risk and surprise costs.

Many people only need read access to logs, metrics, and cost data. Give them that instead of full write access.

How often should we review access?

Run a short review every week. Fifteen minutes is often enough if you stay focused.

Check who has owner, admin, billing, or account-level control. Then review old tokens, old SSH credentials, and accounts for people who left or paused work. Small weekly cuts stop access creep before it piles up.

How do we make a break-glass account safe?

Put it in one controlled account, protect it with strong MFA, and limit who can use it. Then test it on a schedule.

An emergency account only helps if the team knows where it lives, who can reach it, and whether it still works. If nobody tests it, you are guessing.

Should we allow direct admin grants?

Use them rarely. When someone needs direct admin access, write down the reason, name an owner, and set an end date.

That simple rule stops one-time exceptions from turning into permanent admin access. It also makes the next review much faster.

How many access roles does a 10-person team really need?

Keep the role set small. A 10-person team usually needs a handful of simple groups, not dozens of slightly different roles.

If people cannot explain the access model out loud, it is too complex. Simple groups like engineering, deployers, finance, and support work better than a big permission redesign.