{"id":3680,"date":"2025-12-29T18:59:10","date_gmt":"2025-12-29T15:59:10","guid":{"rendered":"https:\/\/www.dchost.com\/blog\/designing-linux-users-groups-and-sudo-on-a-vps-for-multiple-projects-and-teams\/"},"modified":"2025-12-29T18:59:10","modified_gmt":"2025-12-29T15:59:10","slug":"designing-linux-users-groups-and-sudo-on-a-vps-for-multiple-projects-and-teams","status":"publish","type":"post","link":"https:\/\/www.dchost.com\/blog\/en\/designing-linux-users-groups-and-sudo-on-a-vps-for-multiple-projects-and-teams\/","title":{"rendered":"Designing Linux Users, Groups and sudo on a VPS for Multiple Projects and Teams"},"content":{"rendered":"<div class=\"dchost-blog-content-wrapper\"><p>When you put several projects and multiple teams on a single Linux <a href=\"https:\/\/www.dchost.com\/vps\">VPS<\/a>, the technical challenge is rarely CPU or RAM \u2013 it is access management. Who can log in where? Who can deploy? Who can touch production databases? If you do not design a clear user, group and sudo structure from day one, the server slowly turns into a black box of shared passwords, random symlinks and mysterious file owners. At dchost.com, we regularly onboard teams that arrive with exactly this situation and want to clean it up without breaking their live sites.<\/p>\n<p>In this article we will walk through a practical way to design Linux users, groups and sudo rules on a VPS so that multiple projects and teams can work safely side\u2011by\u2011side. We will focus on simple building blocks that scale: per\u2011person accounts, project groups, role\u2011based sudo and predictable directory ownership. You can apply the same ideas whether you run a single VPS at dchost.com or a fleet of servers behind a load balancer. The goal is straightforward: a layout that is easy to explain on a whiteboard, easy to automate and easy to audit after six months.<\/p>\n<div id=\"toc_container\" class=\"toc_transparent no_bullets\"><p class=\"toc_title\">\u0130&ccedil;indekiler<\/p><ul class=\"toc_list\"><li><a href=\"#Why_User_Group_and_sudo_Design_Matters_on_a_MultiProject_VPS\"><span class=\"toc_number toc_depth_1\">1<\/span> Why User, Group and sudo Design Matters on a Multi\u2011Project VPS<\/a><\/li><li><a href=\"#Core_Building_Blocks_Users_Groups_Permissions_and_sudo\"><span class=\"toc_number toc_depth_1\">2<\/span> Core Building Blocks: Users, Groups, Permissions and sudo<\/a><ul><li><a href=\"#Users_Human_vs_service_accounts\"><span class=\"toc_number toc_depth_2\">2.1<\/span> Users: Human vs service accounts<\/a><\/li><li><a href=\"#Groups_Shared_permissions\"><span class=\"toc_number toc_depth_2\">2.2<\/span> Groups: Shared permissions<\/a><\/li><li><a href=\"#Permissions_and_umask\"><span class=\"toc_number toc_depth_2\">2.3<\/span> Permissions and umask<\/a><\/li><li><a href=\"#sudo_Controlled_privilege_escalation\"><span class=\"toc_number toc_depth_2\">2.4<\/span> sudo: Controlled privilege escalation<\/a><\/li><\/ul><\/li><li><a href=\"#Designing_an_Architecture_for_Multiple_Projects_and_Teams\"><span class=\"toc_number toc_depth_1\">3<\/span> Designing an Architecture for Multiple Projects and Teams<\/a><ul><li><a href=\"#Step_1_Map_your_reality\"><span class=\"toc_number toc_depth_2\">3.1<\/span> Step 1: Map your reality<\/a><\/li><li><a href=\"#Step_2_Define_user_types\"><span class=\"toc_number toc_depth_2\">3.2<\/span> Step 2: Define user types<\/a><\/li><li><a href=\"#Step_3_Define_group_types\"><span class=\"toc_number toc_depth_2\">3.3<\/span> Step 3: Define group types<\/a><\/li><li><a href=\"#Step_4_Plan_directory_layout\"><span class=\"toc_number toc_depth_2\">3.4<\/span> Step 4: Plan directory layout<\/a><\/li><li><a href=\"#Step_5_Decide_your_sudo_policy\"><span class=\"toc_number toc_depth_2\">3.5<\/span> Step 5: Decide your sudo policy<\/a><\/li><\/ul><\/li><li><a href=\"#Concrete_Example_One_VPS_Three_Projects_Two_Teams\"><span class=\"toc_number toc_depth_1\">4<\/span> Concrete Example: One VPS, Three Projects, Two Teams<\/a><ul><li><a href=\"#Users\"><span class=\"toc_number toc_depth_2\">4.1<\/span> Users<\/a><\/li><li><a href=\"#Groups\"><span class=\"toc_number toc_depth_2\">4.2<\/span> Groups<\/a><\/li><li><a href=\"#Directory_ownership\"><span class=\"toc_number toc_depth_2\">4.3<\/span> Directory ownership<\/a><\/li><li><a href=\"#Group_membership\"><span class=\"toc_number toc_depth_2\">4.4<\/span> Group membership<\/a><\/li><li><a href=\"#sudo_rules\"><span class=\"toc_number toc_depth_2\">4.5<\/span> sudo rules<\/a><\/li><\/ul><\/li><li><a href=\"#Implementing_the_Design_StepbyStep_on_Linux\"><span class=\"toc_number toc_depth_1\">5<\/span> Implementing the Design Step\u2011by\u2011Step on Linux<\/a><ul><li><a href=\"#1_Create_groups\"><span class=\"toc_number toc_depth_2\">5.1<\/span> 1. Create groups<\/a><\/li><li><a href=\"#2_Create_personal_users\"><span class=\"toc_number toc_depth_2\">5.2<\/span> 2. Create personal users<\/a><\/li><li><a href=\"#3_Create_deploy_users\"><span class=\"toc_number toc_depth_2\">5.3<\/span> 3. Create deploy users<\/a><\/li><li><a href=\"#4_Create_project_directories_and_set_permissions\"><span class=\"toc_number toc_depth_2\">5.4<\/span> 4. Create project directories and set permissions<\/a><\/li><li><a href=\"#5_Adjust_umask_for_group_collaboration\"><span class=\"toc_number toc_depth_2\">5.5<\/span> 5. Adjust umask for group collaboration<\/a><\/li><li><a href=\"#6_Configure_rolebased_sudo\"><span class=\"toc_number toc_depth_2\">5.6<\/span> 6. Configure role\u2011based sudo<\/a><ul><li><a href=\"#Web_operations_sudo_rules\"><span class=\"toc_number toc_depth_3\">5.6.1<\/span> Web operations sudo rules<\/a><\/li><li><a href=\"#Database_operations_sudo_rules\"><span class=\"toc_number toc_depth_3\">5.6.2<\/span> Database operations sudo rules<\/a><\/li><li><a href=\"#Ops_full_admin_sudo_rules\"><span class=\"toc_number toc_depth_3\">5.6.3<\/span> Ops \/ full admin sudo rules<\/a><\/li><\/ul><\/li><li><a href=\"#7_Tie_it_into_your_security_hardening\"><span class=\"toc_number toc_depth_2\">5.7<\/span> 7. Tie it into your security hardening<\/a><\/li><\/ul><\/li><li><a href=\"#Security_Best_Practices_and_Common_Pitfalls\"><span class=\"toc_number toc_depth_1\">6<\/span> Security Best Practices and Common Pitfalls<\/a><ul><li><a href=\"#Avoid_shared_accounts\"><span class=\"toc_number toc_depth_2\">6.1<\/span> Avoid shared accounts<\/a><\/li><li><a href=\"#Never_fix_permissions_with_777\"><span class=\"toc_number toc_depth_2\">6.2<\/span> Never \u201cfix\u201d permissions with 777<\/a><\/li><li><a href=\"#Disable_direct_root_SSH_login\"><span class=\"toc_number toc_depth_2\">6.3<\/span> Disable direct root SSH login<\/a><\/li><li><a href=\"#Separate_staging_from_production\"><span class=\"toc_number toc_depth_2\">6.4<\/span> Separate staging from production<\/a><\/li><li><a href=\"#Combine_with_a_firewall_and_log_monitoring\"><span class=\"toc_number toc_depth_2\">6.5<\/span> Combine with a firewall and log monitoring<\/a><\/li><\/ul><\/li><li><a href=\"#Scaling_the_Model_From_One_VPS_to_Many\"><span class=\"toc_number toc_depth_1\">7<\/span> Scaling the Model: From One VPS to Many<\/a><ul><li><a href=\"#Standardise_your_pattern\"><span class=\"toc_number toc_depth_2\">7.1<\/span> Standardise your pattern<\/a><\/li><li><a href=\"#Automate_with_Ansible_and_Terraform\"><span class=\"toc_number toc_depth_2\">7.2<\/span> Automate with Ansible and Terraform<\/a><\/li><li><a href=\"#Consider_central_authentication_for_larger_teams\"><span class=\"toc_number toc_depth_2\">7.3<\/span> Consider central authentication for larger teams<\/a><\/li><\/ul><\/li><li><a href=\"#Wrapping_Up_A_Calm_Maintainable_VPS_Access_Model\"><span class=\"toc_number toc_depth_1\">8<\/span> Wrapping Up: A Calm, Maintainable VPS Access Model<\/a><\/li><\/ul><\/div>\n<h2><span id=\"Why_User_Group_and_sudo_Design_Matters_on_a_MultiProject_VPS\">Why User, Group and sudo Design Matters on a Multi\u2011Project VPS<\/span><\/h2>\n<p>On a single\u2011tenant server, it is tempting to create one user such as <code>ubuntu<\/code> or <code>root<\/code>, share its SSH key in the team and move on. On a multi\u2011project VPS used by several developers, agencies or clients, that approach quickly becomes a liability.<\/p>\n<ul>\n<li><strong>No accountability:<\/strong> With shared accounts, you cannot answer simple questions like \u201cWho edited <code>config.php<\/code> yesterday?\u201d or \u201cWho ran that dangerous command?\u201d<\/li>\n<li><strong>Hard permission boundaries:<\/strong> When all code runs under one user, it is hard to keep Project A from reading or overwriting Project B\u2019s files.<\/li>\n<li><strong>Risky operations:<\/strong> If everyone has full <code>sudo<\/code>, a typo in production can drop a database or bring the web stack down.<\/li>\n<li><strong>Painful off\u2011boarding:<\/strong> When someone leaves, you need to rotate keys and passwords everywhere because nothing is truly individual.<\/li>\n<\/ul>\n<p>A clear architecture for Linux users, groups and sudo fixes these problems. You get:<\/p>\n<ul>\n<li><strong>Per\u2011person access:<\/strong> One Unix account per human, their own SSH key, easy off\u2011boarding.<\/li>\n<li><strong>Per\u2011project isolation:<\/strong> Project directories owned by dedicated groups instead of a single catch\u2011all user.<\/li>\n<li><strong>Role\u2011based sudo:<\/strong> Developers can restart PHP\u2011FPM or Nginx without being able to reconfigure the firewall or mount disks.<\/li>\n<li><strong>Cleaner deployments:<\/strong> CI pipelines and automation tools use well\u2011defined service users with minimal permissions.<\/li>\n<\/ul>\n<p>If you are setting up a brand\u2011new VPS at dchost.com, this should sit right next to your first\u2011day tasks like updates and firewall rules. Our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/yeni-vpste-ilk-24-saat-guncelleme-guvenlik-duvari-ve-kullanici-hesaplari\/\">the first 24 hours on a new VPS (updates, firewall and users)<\/a> pairs nicely with the concepts we will cover here.<\/p>\n<h2><span id=\"Core_Building_Blocks_Users_Groups_Permissions_and_sudo\">Core Building Blocks: Users, Groups, Permissions and sudo<\/span><\/h2>\n<p>Before designing the architecture, it is worth revisiting the basic Linux concepts we are going to combine.<\/p>\n<h3><span id=\"Users_Human_vs_service_accounts\">Users: Human vs service accounts<\/span><\/h3>\n<p>A <strong>user<\/strong> in Linux is an identity: it owns files and can run processes. On a multi\u2011project VPS you usually have two types:<\/p>\n<ul>\n<li><strong>Human users:<\/strong> One account per person, like <code>alice<\/code>, <code>bob<\/code>. They log in over SSH, use shells and <code>sudo<\/code>.<\/li>\n<li><strong>Service users:<\/strong> Accounts such as <code>nginx<\/code> or <code>deploy<\/code> used by daemons and CI\/CD tools. Often they do not have a real shell or password.<\/li>\n<\/ul>\n<h3><span id=\"Groups_Shared_permissions\">Groups: Shared permissions<\/span><\/h3>\n<p>A <strong>group<\/strong> is a way to give several users the same access to some resources. On a multi\u2011project VPS, groups are your main tool for representing:<\/p>\n<ul>\n<li><strong>Projects:<\/strong> <code>proj_blog<\/code>, <code>proj_api<\/code><\/li>\n<li><strong>Teams:<\/strong> <code>team_frontend<\/code>, <code>team_ops<\/code><\/li>\n<li><strong>Roles:<\/strong> <code>sudo_web<\/code> for web stack operations, <code>sudo_db<\/code> for DB operations<\/li>\n<\/ul>\n<p>Permissions are then applied to these groups at the directory and sudo level.<\/p>\n<h3><span id=\"Permissions_and_umask\">Permissions and umask<\/span><\/h3>\n<p>Linux file permissions define what the <strong>owner<\/strong>, <strong>group<\/strong> and <strong>others<\/strong> can do: read (<code>r<\/code>), write (<code>w<\/code>), execute (<code>x<\/code>). For example, <code>750<\/code> means:<\/p>\n<ul>\n<li>Owner: <code>rwx<\/code> (7)<\/li>\n<li>Group: <code>r-x<\/code> (5)<\/li>\n<li>Others: <code>---<\/code> (0)<\/li>\n<\/ul>\n<p>We will rely on group permissions heavily, so it is worth revisiting our article <a href=\"https:\/\/www.dchost.com\/blog\/en\/linux-dosya-izinleri-644-755-777-paylasimli-hosting-ve-vps-icin-guvenli-ayarlar\/\">explaining Linux file permissions (644, 755, 777) for shared hosting and VPS<\/a> if you need a refresher.<\/p>\n<p>The <strong>umask<\/strong> controls default permissions of new files. For collaborative project directories, we usually want new files to be writable by the group, not just the owner.<\/p>\n<h3><span id=\"sudo_Controlled_privilege_escalation\">sudo: Controlled privilege escalation<\/span><\/h3>\n<p><code>sudo<\/code> lets a user run commands as another user (often <code>root<\/code>) while tracking who did what. The configuration lives in <code>\/etc\/sudoers<\/code> and <code>\/etc\/sudoers.d\/<\/code>. With sudo you can say things like:<\/p>\n<ul>\n<li>Members of <code>sudo_web<\/code> may restart Nginx and PHP\u2011FPM.<\/li>\n<li>Members of <code>sudo_db<\/code> may run <code>pg_dump<\/code> and <code>mysql<\/code> on production databases.<\/li>\n<li>Everyone else has no sudo at all.<\/li>\n<\/ul>\n<p>Done right, sudo is one of your strongest protection layers on a multi\u2011project VPS.<\/p>\n<h2><span id=\"Designing_an_Architecture_for_Multiple_Projects_and_Teams\">Designing an Architecture for Multiple Projects and Teams<\/span><\/h2>\n<p>Let us design a model that works for 2\u201310 projects and a handful of developers or agencies on the same VPS. We will keep it simple enough to implement manually, but structured enough to automate later.<\/p>\n<h3><span id=\"Step_1_Map_your_reality\">Step 1: Map your reality<\/span><\/h3>\n<p>In a quick planning session, list the things you need to represent on the server:<\/p>\n<ul>\n<li><strong>Projects:<\/strong> For example <code>corporate\u2011site<\/code>, <code>api\u2011backend<\/code>, <code>shop\u2011frontend<\/code>.<\/li>\n<li><strong>Environments:<\/strong> Production, staging, sometimes development.<\/li>\n<li><strong>Teams and roles:<\/strong> Who deploys? Who only reads logs? Who manages databases?<\/li>\n<li><strong>External parties:<\/strong> Any agencies or freelancers that should only touch one project?<\/li>\n<\/ul>\n<p>Write this on a whiteboard. The Linux layout will be a direct translation of this picture.<\/p>\n<h3><span id=\"Step_2_Define_user_types\">Step 2: Define user types<\/span><\/h3>\n<p>We recommend three main types of accounts on the VPS:<\/p>\n<ol>\n<li><strong>Personal users (one per person)<\/strong><br \/>Examples: <code>alice<\/code>, <code>bob<\/code>, <code>carol<\/code>. These accounts own SSH keys and are members of various groups but typically do not own project directories themselves.<\/li>\n<li><strong>Project deploy users<\/strong><br \/>Examples: <code>deploy_corp<\/code>, <code>deploy_api<\/code>. These are used by CI\/CD (GitHub Actions, GitLab, etc.) and sometimes for manual deploys. They own the deployed code, not the personal users.<\/li>\n<li><strong>System\/service users<\/strong><br \/>Examples: <code>nginx<\/code>, <code>mysql<\/code>, <code>redis<\/code>. These are created by your packages and system; we will not modify them except for permission needs.<\/li>\n<\/ol>\n<p>Why separate personal and deploy users? Because code history, release ownership and rollbacks become much simpler. If a developer leaves, you do not have to chown half the filesystem.<\/p>\n<h3><span id=\"Step_3_Define_group_types\">Step 3: Define group types<\/span><\/h3>\n<p>Next, define groups that mirror your projects and roles:<\/p>\n<ul>\n<li><strong>Project groups:<\/strong> One per project per environment, e.g. <code>g_corp_prod<\/code>, <code>g_corp_stage<\/code>, <code>g_api_prod<\/code>.<\/li>\n<li><strong>Role groups for sudo:<\/strong> <code>sudo_web<\/code> (web stack operations), <code>sudo_db<\/code> (DB ops), <code>sudo_ops<\/code> (full operations).<\/li>\n<li><strong>Optional team groups:<\/strong> If you have large teams, you can add <code>team_front<\/code>, <code>team_back<\/code> etc. and then map team groups into project groups, but for most small VPS setups project groups alone are enough.<\/li>\n<\/ul>\n<p>In practice, a developer\u2019s Unix account will end up looking like:<\/p>\n<ul>\n<li>Primary group: <code>alice<\/code> (created with the user).<\/li>\n<li>Supplementary groups: <code>g_corp_prod<\/code>, <code>g_corp_stage<\/code>, <code>sudo_web<\/code>.<\/li>\n<\/ul>\n<h3><span id=\"Step_4_Plan_directory_layout\">Step 4: Plan directory layout<\/span><\/h3>\n<p>A predictable directory structure makes permissions much easier to reason about. A common and clean pattern is:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">\/srv\/\n  corp-site\/\n    prod\/\n      app\/      # code\n      shared\/   # uploads, cache, logs\n    stage\/\n      app\/\n      shared\/\n  api-backend\/\n    prod\/\n      app\/\n      shared\/\n<\/code><\/pre>\n<p>Each <code>prod<\/code> and <code>stage<\/code> directory can then be owned by its project+environment group, such as <code>g_corp_prod<\/code>. Web server config under <code>\/etc\/nginx\/sites-available<\/code> follows the same naming.<\/p>\n<h3><span id=\"Step_5_Decide_your_sudo_policy\">Step 5: Decide your sudo policy<\/span><\/h3>\n<p>Finally, write down who should be able to do what with elevated privileges. A minimal but practical set:<\/p>\n<ul>\n<li><strong>Developers:<\/strong> Can restart services related to their projects (Nginx, PHP\u2011FPM, queue workers), but cannot manage firewall or users.<\/li>\n<li><strong>Ops \/ lead developer:<\/strong> Can manage web stack and database tools, but still not system\u2011wide configuration like disk partitioning.<\/li>\n<li><strong>Root (or a small ops group):<\/strong> Full access for rare operations, possibly only via <code>sudo -i<\/code>, never via direct SSH.<\/li>\n<\/ul>\n<p>We will implement this with role\u2011based sudo groups in a moment.<\/p>\n<h2><span id=\"Concrete_Example_One_VPS_Three_Projects_Two_Teams\">Concrete Example: One VPS, Three Projects, Two Teams<\/span><\/h2>\n<p>Let us build a concrete example to make this more tangible. Imagine a VPS at dchost.com hosting three applications for a small company:<\/p>\n<ul>\n<li><strong>Corporate website:<\/strong> WordPress at <code>corp.example.com<\/code><\/li>\n<li><strong>API backend:<\/strong> Laravel at <code>api.example.com<\/code><\/li>\n<li><strong>Shop frontend:<\/strong> Next.js at <code>shop.example.com<\/code><\/li>\n<\/ul>\n<p>Two teams work on it: Web (WordPress + frontend) and Backend (API + some WordPress integration).<\/p>\n<h3><span id=\"Users\">Users<\/span><\/h3>\n<ul>\n<li>Personal users: <code>alice<\/code> (backend), <code>bob<\/code> (web), <code>carol<\/code> (ops lead).<\/li>\n<li>Deploy users: <code>deploy_corp<\/code>, <code>deploy_api<\/code>, <code>deploy_shop<\/code>.<\/li>\n<\/ul>\n<h3><span id=\"Groups\">Groups<\/span><\/h3>\n<ul>\n<li>Project groups: <code>g_corp_prod<\/code>, <code>g_corp_stage<\/code>, <code>g_api_prod<\/code>, <code>g_shop_prod<\/code>.<\/li>\n<li>Role groups: <code>sudo_web<\/code> (web service control), <code>sudo_db<\/code> (database tools), <code>sudo_ops<\/code> (full operations).<\/li>\n<\/ul>\n<h3><span id=\"Directory_ownership\">Directory ownership<\/span><\/h3>\n<p>We may decide:<\/p>\n<ul>\n<li><code>\/srv\/corp-site\/prod<\/code> owned by <code>deploy_corp:g_corp_prod<\/code>, mode <code>2770<\/code> (setgid, group\u2011writable).<\/li>\n<li><code>\/srv\/api-backend\/prod<\/code> owned by <code>deploy_api:g_api_prod<\/code>, mode <code>2770<\/code>.<\/li>\n<li><code>\/srv\/shop-frontend\/prod<\/code> owned by <code>deploy_shop:g_shop_prod<\/code>, mode <code>2770<\/code>.<\/li>\n<\/ul>\n<p>The setgid bit (<code>2<\/code> in <code>2770<\/code>) ensures newly created files inherit the directory\u2019s group, keeping collaboration simple.<\/p>\n<h3><span id=\"Group_membership\">Group membership<\/span><\/h3>\n<p>We might assign group membership like this:<\/p>\n<ul>\n<li><strong>alice (backend):<\/strong> <code>g_api_prod<\/code>, <code>g_corp_stage<\/code>, <code>sudo_web<\/code>, <code>sudo_db<\/code><\/li>\n<li><strong>bob (web):<\/strong> <code>g_corp_prod<\/code>, <code>g_shop_prod<\/code>, <code>sudo_web<\/code><\/li>\n<li><strong>carol (ops):<\/strong> all project groups + <code>sudo_web<\/code>, <code>sudo_db<\/code>, <code>sudo_ops<\/code><\/li>\n<\/ul>\n<p>Deploy users become members of their respective project groups so that CI\/CD can write into project directories.<\/p>\n<h3><span id=\"sudo_rules\">sudo rules<\/span><\/h3>\n<p>We then declare in <code>\/etc\/sudoers.d\/<\/code> (details in the next section):<\/p>\n<ul>\n<li><code>%sudo_web<\/code> may run <code>systemctl restart nginx<\/code>, <code>php-fpm<\/code>, queue workers.<\/li>\n<li><code>%sudo_db<\/code> may run <code>mysql<\/code>, <code>mysqldump<\/code>, <code>psql<\/code>, <code>pg_dump<\/code>.<\/li>\n<li><code>%sudo_ops<\/code> has <code>ALL=(ALL) ALL<\/code> for rare operations.<\/li>\n<\/ul>\n<p>This gives a clean story in security audits: web team changes web services; backend team changes API and DB via well\u2011defined tools; ops can do everything, but rarely does so.<\/p>\n<h2><span id=\"Implementing_the_Design_StepbyStep_on_Linux\">Implementing the Design Step\u2011by\u2011Step on Linux<\/span><\/h2>\n<p>Now let us translate the architecture into real commands. The examples assume a Debian\/Ubuntu\u2011like system, but the concepts apply to other Linux distributions as well.<\/p>\n<h3><span id=\"1_Create_groups\">1. Create groups<\/span><\/h3>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\"># Project groups\nsudo groupadd g_corp_prod\nsudo groupadd g_corp_stage\nsudo groupadd g_api_prod\nsudo groupadd g_shop_prod\n\n# Role \/ sudo groups\nsudo groupadd sudo_web\nsudo groupadd sudo_db\nsudo groupadd sudo_ops\n<\/code><\/pre>\n<h3><span id=\"2_Create_personal_users\">2. Create personal users<\/span><\/h3>\n<p>Create users without sudo yet; we will handle privileges via group membership.<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo adduser alice\nsudo adduser bob\nsudo adduser carol\n<\/code><\/pre>\n<p>Then attach them to the relevant project and sudo groups:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo usermod -aG g_api_prod,g_corp_stage,sudo_web,sudo_db alice\nsudo usermod -aG g_corp_prod,g_shop_prod,sudo_web bob\nsudo usermod -aG g_corp_prod,g_corp_stage,g_api_prod,g_shop_prod,sudo_web,sudo_db,sudo_ops carol\n<\/code><\/pre>\n<p>For SSH best practices (keys instead of passwords, no direct root login), pair this with the approach from our article on <a href=\"https:\/\/www.dchost.com\/blog\/en\/ssh-anahtar-yonetimi-ve-yetki-paylasimi-kucuk-ekipler-icin-guvenli-vps-erisimi\/\">SSH key management and access sharing for small teams<\/a>.<\/p>\n<h3><span id=\"3_Create_deploy_users\">3. Create deploy users<\/span><\/h3>\n<p>Deploy users typically do not need interactive shells. We can give them <code>\/usr\/sbin\/nologin<\/code> or <code>\/bin\/false<\/code> as their shell and restrict authentication to SSH keys used by CI.<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo useradd -m -s \/usr\/sbin\/nologin -G g_corp_prod deploy_corp\nsudo useradd -m -s \/usr\/sbin\/nologin -G g_api_prod deploy_api\nsudo useradd -m -s \/usr\/sbin\/nologin -G g_shop_prod deploy_shop\n<\/code><\/pre>\n<p>Place CI\/CD public keys into <code>\/home\/deploy_*\/.ssh\/authorized_keys<\/code> and lock these accounts down to deployment commands if necessary using <code>command=\"...\"<\/code> in the authorized key entry.<\/p>\n<h3><span id=\"4_Create_project_directories_and_set_permissions\">4. Create project directories and set permissions<\/span><\/h3>\n<p>We now prepare the directory structure under <code>\/srv<\/code>:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo mkdir -p \/srv\/corp-site\/{prod,stage}\/{app,shared}\nsudo mkdir -p \/srv\/api-backend\/prod\/{app,shared}\nsudo mkdir -p \/srv\/shop-frontend\/prod\/{app,shared}\n\n# Ownership: deploy user as owner, project group as group\nsudo chown -R deploy_corp:g_corp_prod \/srv\/corp-site\/prod\nsudo chown -R deploy_corp:g_corp_stage \/srv\/corp-site\/stage\nsudo chown -R deploy_api:g_api_prod \/srv\/api-backend\/prod\nsudo chown -R deploy_shop:g_shop_prod \/srv\/shop-frontend\/prod\n\n# Permissions: setgid (2), group-writable, no access for others\nsudo chmod -R 2770 \/srv\/corp-site\nsudo chmod -R 2770 \/srv\/api-backend\nsudo chmod -R 2770 \/srv\/shop-frontend\n<\/code><\/pre>\n<p>The combination of <code>chown<\/code> and <code>chmod 2770<\/code> ensures:<\/p>\n<ul>\n<li>Only users in the project group (plus the deploy user) can read and write.<\/li>\n<li>New files inherit the group, so collaboration does not break.<\/li>\n<li>Other users on the VPS cannot even list the directories.<\/li>\n<\/ul>\n<h3><span id=\"5_Adjust_umask_for_group_collaboration\">5. Adjust umask for group collaboration<\/span><\/h3>\n<p>To ensure new files are group\u2011writable, you can adjust the <code>umask<\/code> for deploy users and interactive shells. For example, set <code>umask 0002<\/code> in <code>\/etc\/profile.d\/umask.sh<\/code>:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">echo 'umask 0002' | sudo tee \/etc\/profile.d\/umask.sh &gt; \/dev\/null\n<\/code><\/pre>\n<p>Now, files created inside project directories will typically be <code>rw-rw----<\/code> (660) instead of <code>rw-r-----<\/code> (640), which is what you want for shared workspaces.<\/p>\n<h3><span id=\"6_Configure_rolebased_sudo\">6. Configure role\u2011based sudo<\/span><\/h3>\n<p>Instead of editing <code>\/etc\/sudoers<\/code> directly, we recommend using separate files under <code>\/etc\/sudoers.d\/<\/code>. Always edit with <code>visudo -f<\/code> so syntax errors do not break sudo entirely.<\/p>\n<h4><span id=\"Web_operations_sudo_rules\">Web operations sudo rules<\/span><\/h4>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo visudo -f \/etc\/sudoers.d\/web-ops\n<\/code><\/pre>\n<p>Example content:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">%sudo_web ALL=(root) NOPASSWD: \n  \/bin\/systemctl reload nginx, \n  \/bin\/systemctl restart nginx, \n  \/bin\/systemctl reload php*-fpm.service, \n  \/bin\/systemctl restart php*-fpm.service, \n  \/bin\/systemctl restart supervisor, \n  \/usr\/bin\/journalctl -u nginx -u php*-fpm -u supervisor\n<\/code><\/pre>\n<p>This allows web team members to restart web services and inspect logs without giving them unrestricted root.<\/p>\n<h4><span id=\"Database_operations_sudo_rules\">Database operations sudo rules<\/span><\/h4>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo visudo -f \/etc\/sudoers.d\/db-ops\n<\/code><\/pre>\n<p>Example content:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">%sudo_db ALL=(root) NOPASSWD: \n  \/usr\/bin\/mysql, \n  \/usr\/bin\/mysqldump, \n  \/usr\/bin\/psql, \n  \/usr\/bin\/pg_dump\n<\/code><\/pre>\n<p>You might choose to remove <code>NOPASSWD<\/code> here for an extra confirmation step before running DB tools.<\/p>\n<h4><span id=\"Ops_full_admin_sudo_rules\">Ops \/ full admin sudo rules<\/span><\/h4>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">sudo visudo -f \/etc\/sudoers.d\/ops\n<\/code><\/pre>\n<p>Example content:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">%sudo_ops ALL=(ALL) ALL\n<\/code><\/pre>\n<p>Keep membership in <code>sudo_ops<\/code> very limited (for example only your main ops lead). Everyone else should work through the more limited <code>sudo_web<\/code> and <code>sudo_db<\/code> roles.<\/p>\n<h3><span id=\"7_Tie_it_into_your_security_hardening\">7. Tie it into your security hardening<\/span><\/h3>\n<p>User, group and sudo design does not live in a vacuum. It should be part of a broader server security baseline: SSH hardening, firewall rules, automatic updates and log monitoring. Our <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-guvenlik-sertlestirme-kontrol-listesi-sshd_config-fail2ban-ve-root-erisimini-kapatmak\/\">VPS security hardening checklist with sshd_config, Fail2ban and disabling root SSH<\/a> goes through these steps in detail and complements the architecture we have just built.<\/p>\n<h2><span id=\"Security_Best_Practices_and_Common_Pitfalls\">Security Best Practices and Common Pitfalls<\/span><\/h2>\n<p>Even with a good design, a few habits can quietly weaken your model over time. Here are issues we see often when helping dchost.com customers clean up existing VPS setups.<\/p>\n<h3><span id=\"Avoid_shared_accounts\">Avoid shared accounts<\/span><\/h3>\n<p>It is tempting to let everyone use a single <code>deploy<\/code> or <code>admin<\/code> account for convenience. The cost comes later when you cannot trace who did what or when off\u2011boarding requires changing keys and passwords in many places. Keep personal accounts for humans; restrict shared accounts to CI\/CD and automation, and protect them carefully.<\/p>\n<h3><span id=\"Never_fix_permissions_with_777\">Never \u201cfix\u201d permissions with 777<\/span><\/h3>\n<p>When a web app fails with a \u201cpermission denied\u201d error, a common quick\u2011and\u2011dirty fix is to run:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">chmod -R 777 \/srv\/corp-site\/prod\n<\/code><\/pre>\n<p>This gives everyone full control, including any exploited web process or untrusted user on the server. Instead, use group\u2011based permissions (like <code>2770<\/code>) and proper group membership. If you are unsure how far to go, revisit our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/linux-dosya-izinleri-644-755-777-paylasimli-hosting-ve-vps-icin-guvenli-ayarlar\/\">safe Linux file permissions on VPS<\/a>.<\/p>\n<h3><span id=\"Disable_direct_root_SSH_login\">Disable direct root SSH login<\/span><\/h3>\n<p>Root should exist but not be directly reachable via SSH. Use:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\">PermitRootLogin no\n<\/code><\/pre>\n<p>in <code>\/etc\/ssh\/sshd_config<\/code>, then restart SSH. Admins log in as themselves and use <code>sudo<\/code>. That way, logs show which person did what, not just \u201croot\u201d.<\/p>\n<h3><span id=\"Separate_staging_from_production\">Separate staging from production<\/span><\/h3>\n<p>On small VPS setups it is common to put staging and production on the same machine. If you do, at least give them different groups (<code>g_corp_stage<\/code> vs <code>g_corp_prod<\/code>) and watch who is in each. Do not automatically grant staging access to everyone who touches production, and vice\u2011versa. That way, a risky experiment in staging is less likely to leak into production directories.<\/p>\n<h3><span id=\"Combine_with_a_firewall_and_log_monitoring\">Combine with a firewall and log monitoring<\/span><\/h3>\n<p>Even the best user architecture will not help if SSH is open to the world with password logins, no firewall and no log alerts. We recommend pairing your access structure with a firewall configuration (ufw, firewalld or nftables) and basic monitoring. Our article on <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-sunucularda-guvenlik-duvari-yapilandirma-ufw-firewalld-ve-iptables\/\">firewall configuration on VPS servers<\/a> shows how to lock down network access in a practical way.<\/p>\n<h2><span id=\"Scaling_the_Model_From_One_VPS_to_Many\">Scaling the Model: From One VPS to Many<\/span><\/h2>\n<p>As your projects and teams grow, this architecture should scale horizontally without rethinking everything.<\/p>\n<h3><span id=\"Standardise_your_pattern\">Standardise your pattern<\/span><\/h3>\n<p>Once you are happy with the layout on one VPS, document your conventions:<\/p>\n<ul>\n<li>How project groups are named (<code>g_project_env<\/code>).<\/li>\n<li>How deploy users are named (<code>deploy_project<\/code>).<\/li>\n<li>Where code and shared data live (<code>\/srv\/project\/env\/{app,shared}<\/code>).<\/li>\n<li>Which sudo roles exist and what they allow (<code>sudo_web<\/code>, <code>sudo_db<\/code>, <code>sudo_ops<\/code>).<\/li>\n<\/ul>\n<p>Then reuse this pattern on every new VPS or <a href=\"https:\/\/www.dchost.com\/dedicated-server\">dedicated server<\/a> you provision at dchost.com. Consistency makes troubleshooting and onboarding much easier.<\/p>\n<h3><span id=\"Automate_with_Ansible_and_Terraform\">Automate with Ansible and Terraform<\/span><\/h3>\n<p>Manually running <code>useradd<\/code>, <code>groupadd<\/code> and <code>chown<\/code> is fine for the first server. When you have several, you will want automation. A common pattern:<\/p>\n<ul>\n<li>Use Terraform (or similar) to provision new VPS instances.<\/li>\n<li>Use Ansible to configure users, groups, sudoers, directories and permissions based on structured variables (projects, environments, teams).<\/li>\n<\/ul>\n<p>We have a dedicated guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/terraform-ve-ansible-ile-vps-otomasyonu-ayni-sunucuyu-tek-tusla-kurmak\/\">automating VPS setup with Terraform and Ansible<\/a> where the same concepts are turned into reusable playbooks. Your user\/group architecture is a perfect fit for this style of infrastructure\u2011as\u2011code.<\/p>\n<h3><span id=\"Consider_central_authentication_for_larger_teams\">Consider central authentication for larger teams<\/span><\/h3>\n<p>If you reach the stage where you manage dozens of servers and tens of developers, consider central authentication (LDAP\/FreeIPA, SSSD, or managed directory services). Your logical design stays the same \u2013 one user per person, project\/role groups, sudo roles \u2013 but group membership is now managed centrally and pushed to all servers.<\/p>\n<h2><span id=\"Wrapping_Up_A_Calm_Maintainable_VPS_Access_Model\">Wrapping Up: A Calm, Maintainable VPS Access Model<\/span><\/h2>\n<p>A Linux VPS does not have to be a mystery box that only one senior developer can touch. With a deliberate design for users, groups and sudo, you get a structure you can explain in a five\u2011minute whiteboard session: \u201cThese are our project groups, those are our deploy users, here is what <code>sudo_web<\/code> can do, here is where each app lives under <code>\/srv<\/code>.\u201d That clarity pays off whenever you onboard a new colleague, respond to a security review or need to hand over operations to another team.<\/p>\n<p>At dchost.com we see the same pattern over and over: teams that invest a bit of time early in a clean access model spend far less energy later firefighting permission issues, mysterious ownership problems or risky blanket sudo rights. If you are about to launch new projects or migrate from shared hosting to one of our VPS or dedicated server plans, this is an ideal moment to put these ideas in place. Pair this architecture with our <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-guvenlik-sertlestirme-kontrol-listesi-sshd_config-fail2ban-ve-root-erisimini-kapatmak\/\">VPS hardening checklist<\/a> and, if you automate, with <a href=\"https:\/\/www.dchost.com\/blog\/en\/terraform-ve-ansible-ile-vps-otomasyonu-ayni-sunucuyu-tek-tusla-kurmak\/\">Terraform and Ansible based VPS automation<\/a>. The result is a server layout that feels calm, predictable and ready to grow with your projects and teams.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>When you put several projects and multiple teams on a single Linux VPS, the technical challenge is rarely CPU or RAM \u2013 it is access management. Who can log in where? Who can deploy? Who can touch production databases? If you do not design a clear user, group and sudo structure from day one, the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3681,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-3680","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-teknoloji"],"_links":{"self":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/3680","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/comments?post=3680"}],"version-history":[{"count":0,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/3680\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media\/3681"}],"wp:attachment":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media?parent=3680"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/categories?post=3680"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/tags?post=3680"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}