{"id":14238,"date":"2025-12-27T12:09:45","date_gmt":"2025-12-27T06:39:45","guid":{"rendered":"https:\/\/www.youstable.com\/blog\/?p=14238"},"modified":"2025-12-27T12:09:47","modified_gmt":"2025-12-27T06:39:47","slug":"create-git-on-linux-server","status":"publish","type":"post","link":"https:\/\/www.youstable.com\/blog\/create-git-on-linux-server","title":{"rendered":"How to Create Git on Linux Server for CI\/CD Workflows"},"content":{"rendered":"\n<p><strong>To create Git on a Linux server<\/strong>, install Git, add a dedicated \u201cgit\u201d user, initialize a bare repository for collaboration, enable SSH key access, and push from your local machine. <\/p>\n\n\n\n<p>This setup provides a secure, lightweight Git server over SSH, ideal for small teams, CI\/CD, and private version control without the overhead of full platforms.<\/p>\n\n\n\n<p>In this guide, you\u2019ll learn how to create Git on a Linux server the right way\u2014fast, secure, and production-ready. We\u2019ll cover installing Git, creating a bare repository, setting SSH access, managing permissions for teams, and hardening the server.<\/p>\n\n\n\n<p>Whether you use Ubuntu, Debian, or RHEL-based systems, the steps below are clear and beginner-friendly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"what-youll-build-a-lightweight-git-server-over-ssh\"><strong>What You\u2019ll Build: A Lightweight Git Server Over SSH<\/strong><\/h2>\n\n\n\n<p>A minimal Git server uses SSH for transport and a dedicated user for repositories. You push and pull using SSH URLs, and each project is a \u201cbare\u201d repository on disk. This is the most stable, secure, and resource-efficient way to host private Git without installing heavy web interfaces.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"prerequisites\"><strong>Prerequisites<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A Linux server (Ubuntu\/Debian or RHEL\/CentOS\/AlmaLinux\/Rocky)<\/li>\n\n\n\n<li>Root or sudo access<\/li>\n\n\n\n<li>OpenSSH server installed and reachable on port 22<\/li>\n\n\n\n<li>Local machine with Git installed<\/li>\n\n\n\n<li>Optional: a domain or static IP for cleaner remote URLs<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-1-install-git\"><strong>Step 1: Install Git<\/strong><\/h2>\n\n\n\n<p>Install the latest stable Git from your distro\u2019s repos. For most use cases, the packaged version is sufficient and secure.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"ubuntu-debian\"><strong>Ubuntu\/Debian<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update\nsudo apt install -y git<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"rhel-centos-almalinux-rocky\"><strong>RHEL\/CentOS\/AlmaLinux\/Rocky<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y git\n# or on older systems:\n# sudo <a href=\"https:\/\/www.youstable.com\/blog\/install-yum-on-linux\/\">yum install<\/a> -y git<\/code><\/pre>\n\n\n\n<p>Verify installation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git --version<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-2-create-a-dedicated-git-user\"><strong>Step 2: Create a Dedicated Git User<\/strong><\/h2>\n\n\n\n<p>Running Git under a dedicated account improves security and organization. We\u2019ll use the username \u201cgit.\u201d<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo adduser --system --shell \/bin\/bash --group --disabled-password git\n# Create home if not present and set safe permissions\nsudo mkdir -p \/home\/git\nsudo chown -R git:git \/home\/git\nsudo chmod 750 \/home\/git<\/code><\/pre>\n\n\n\n<p>The \u201cgit\u201d user will own all repositories. Team members will <a href=\"https:\/\/www.youstable.com\/blog\/how-to-connect-to-server-via-ssh\/\">connect via SSH<\/a> to this account or via their own users added to a shared group (explained later).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-3-initialize-a-bare-repository-on-the-server\"><strong>Step 3: Initialize a Bare Repository on the Server<\/strong><\/h2>\n\n\n\n<p>Bare repositories store Git data without a working tree and are designed for collaboration.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo -u git mkdir -p \/home\/git\/repos\nsudo -u git git init --bare --shared=group \/home\/git\/repos\/myapp.git<\/code><\/pre>\n\n\n\n<p>The &#8211;bare flag creates a repository that\u2019s optimized for pushing and pulling. The &#8211;shared=group helps when multiple users (in the same group) contribute to the repo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-4-configure-ssh-key-access\"><strong>Step 4: Configure SSH Key Access<\/strong><\/h2>\n\n\n\n<p>Use <a href=\"https:\/\/www.youstable.com\/blog\/how-to-add-ssh-keys-to-github-account\/\">SSH keys<\/a> for secure, passwordless access. Generate a key on your local machine and upload it to the server.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"on-your-local-machine\"><strong>On your local machine<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh-keygen -t ed25519 -C \"you@example.com\"\n# press enter to accept defaults and set a passphrase<\/code><\/pre>\n\n\n\n<p>Copy your public key to the server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh-copy-id git@your-server-ip\n# or manually:\n# cat ~\/.ssh\/id_ed25519.pub | ssh git@your-server-ip \"mkdir -p ~\/.ssh &amp;&amp; cat &gt;&gt; ~\/.ssh\/authorized_keys &amp;&amp; chmod 700 ~\/.ssh &amp;&amp; chmod 600 ~\/.ssh\/authorized_keys\"<\/code><\/pre>\n\n\n\n<p>Test login:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh git@your-server-ip\n# you should land in the git user's home (no sudo privileges)<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-5-push-an-existing-project-to-the-server\"><strong>Step 5: Push an Existing Project to the Server<\/strong><\/h2>\n\n\n\n<p>From your local repository, add the remote and push. Replace host\/IP and repo name accordingly.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/path\/to\/local\/project\ngit init\ngit add .\ngit commit -m \"Initial commit\"\n\ngit remote add origin git@your-server-ip:\/home\/git\/repos\/myapp.git\ngit push -u origin main\n# or 'master' depending on your branch name<\/code><\/pre>\n\n\n\n<p>Teammates can now clone the repo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone git@your-server-ip:\/home\/git\/repos\/myapp.git<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-6-manage-collaborators-and-permissions-group-based\"><strong>Step 6: Manage Collaborators and Permissions (Group-Based)<\/strong><\/h2>\n\n\n\n<p>For multiple users, use a shared Unix group to keep permissions sane and predictable.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># On the server:\nsudo groupadd gitgroup\nsudo usermod -aG gitgroup git\n\n# Add real users (already existing) to the group:\nsudo usermod -aG gitgroup dev1\nsudo usermod -aG gitgroup dev2\n\n# Ensure repos are owned by group and writable:\nsudo chown -R git:gitgroup \/home\/git\/repos\nsudo chmod -R g+ws \/home\/git\/repos\n\n# Set repository-level config to respect group sharing:\nsudo -u git git --git-dir=\/home\/git\/repos\/myapp.git config core.sharedRepository group<\/code><\/pre>\n\n\n\n<p>Developers can use their own SSH accounts and clone via their usernames, or you can centrally manage keys in the git user\u2019s authorized_keys. For high security, restrict the git account to Git-only commands using git-shell.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-7-harden-and-secure-your-git-server\"><strong>Step 7: Harden and Secure Your Git Server<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"firewall\"><strong>Firewall<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Ubuntu\/Debian (UFW):\nsudo ufw allow OpenSSH\nsudo ufw enable\nsudo ufw status\n\n# RHEL family (firewalld):\nsudo firewall-cmd --permanent --add-service=ssh\nsudo firewall-cmd --reload<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"ssh-hardening\"><strong>SSH Hardening<\/strong><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo cp \/etc\/ssh\/sshd_config \/etc\/ssh\/sshd_config.bak\nsudo sed -i 's\/^#PasswordAuthentication yes\/PasswordAuthentication no\/' \/etc\/ssh\/sshd_config\nsudo sed -i 's\/^#PubkeyAuthentication yes\/PubkeyAuthentication yes\/' \/etc\/ssh\/sshd_config\n# Optionally change port, disable root login:\n# Port 2222\n# PermitRootLogin no\nsudo systemctl reload sshd<\/code><\/pre>\n\n\n\n<p>Consider <a href=\"https:\/\/www.youstable.com\/blog\/what-is-fail2ban-on-linux-server\/\">Fail2ban for brute-force protection and keep your server<\/a> patched. If SELinux is enforcing, ensure repository paths have correct contexts or place them under \/home\/git with default contexts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"troubleshooting-common-issues\"><strong>Troubleshooting Common Issues<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Permission denied (publickey):<\/strong> Ensure your SSH key is in authorized_keys and file permissions on ~\/.ssh are correct (700 directory, 600 files).<\/li>\n\n\n\n<li><strong>fatal: could not create work tree dir: <\/strong>Check directory ownership and that group write is enabled on the repo path.<\/li>\n\n\n\n<li><strong>Repository not found: <\/strong>Verify the exact path to the bare repo and that it ends with .git.<\/li>\n\n\n\n<li><strong>Wrong default branch:<\/strong> Use git symbolic-ref HEAD refs\/heads\/main inside the bare repo or push your desired branch first with -u.<\/li>\n\n\n\n<li><strong>Slow pushes: <\/strong>Run git gc periodically, and check network latency or CPU\/storage performance.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"optional-lightweight-web-interfaces-when-you-need-more\"><strong>Optional: Lightweight Web Interfaces (When You Need More)<\/strong><\/h2>\n\n\n\n<p><strong>If you want a UI without going full enterprise, consider:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Gitea: <\/strong>Very light, easy install, built-in issues, PRs, and CI integrations. Great for small to mid teams.<\/li>\n\n\n\n<li><strong>Gitolite:<\/strong> SSH-key managed permissions and repo control; no web UI, but powerful ACLs.<\/li>\n\n\n\n<li><strong>GitLab: <\/strong>Feature-rich DevOps platform; requires more CPU\/RAM and maintenance.<\/li>\n<\/ul>\n\n\n\n<p>For many teams, a pure SSH Git server is enough. Add a web interface later if you need code reviews, issues, or CI\/CD dashboards.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"best-practices-for-production\"><strong>Best Practices for Production<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Backups:<\/strong> Snapshot \/home\/git and regularly rsync or use object storage. Test restores!<\/li>\n\n\n\n<li><strong>Hooks: <\/strong>Use server-side hooks (pre-receive, post-receive) for linting, CI triggers, or branch protections.<\/li>\n\n\n\n<li><strong>Access hygiene: <\/strong>Remove stale keys, rotate critical keys, and audit authorized_keys periodically.<\/li>\n\n\n\n<li><strong>Resource upkeep: <\/strong>Keep Git updated, run git gc on large repos, and monitor disk I\/O and inode usage.<\/li>\n\n\n\n<li><strong>Isolation: <\/strong>Run on a dedicated VPS. Avoid mixing critical apps and repos on the same box.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"why-host-your-git-on-a-youstable-vps\"><strong>Why Host Your Git on a YouStable VPS<\/strong><\/h2>\n\n\n\n<p>Hosting your Git server on a YouStable VPS gives you predictable performance, full root access, snapshots, and DDoS protection options\u2014perfect for secure, private version control. Spin up an Ubuntu or AlmaLinux instance in minutes, harden SSH, and deploy your Git repos with confidence on infrastructure tuned for developers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"full-example-from-zero-to-first-push\"><strong>Full Example: From Zero to First Push<\/strong><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code># 1) Install Git\nsudo apt update &amp;&amp; sudo apt install -y git  # Debian\/Ubuntu\n\n# 2) Create git user and repo path\nsudo adduser --system --shell \/bin\/bash --group --disabled-password git\nsudo -u git mkdir -p \/home\/git\/repos\n\n# 3) Create a bare repository\nsudo -u git git init --bare --shared=group \/home\/git\/repos\/myapp.git\n\n# 4) Harden SSH (keys only)\nssh-keygen -t ed25519 -C \"you@example.com\"\nssh-copy-id git@your-server-ip\n# optional: disable password auth in sshd_config then reload\n\n# 5) Push from local\ncd \/path\/to\/local\/project\ngit init\ngit add .\ngit commit -m \"Initial commit\"\ngit remote add origin git@your-server-ip:\/home\/git\/repos\/myapp.git\ngit push -u origin main<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"key-takeaways\"><strong>Key Takeaways<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use a dedicated git user and bare repositories for clean, safe hosting.<\/li>\n\n\n\n<li>Prefer SSH keys and disable password logins for security.<\/li>\n\n\n\n<li>Use a shared group and core.sharedRepository for multi-user collaboration.<\/li>\n\n\n\n<li>Harden with firewalls, backups, and periodic maintenance.<\/li>\n\n\n\n<li>Scale up to Gitea or GitLab only if you need advanced workflows and UI.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"faqs\"><strong>FAQs<\/strong><\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1765963068983\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"1-whats-the-simplest-way-to-set-up-a-private-git-server-on-linux\">1. <strong>What\u2019s the simplest way to set up a private Git server on Linux?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Install Git, create a dedicated git user, initialize a bare repository, and use SSH keys for access. Push and pull with SSH URLs. This approach is secure, fast, and requires minimal resources.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765963090947\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"2-should-i-use-a-bare-or-non-bare-repository-on-the-server\">2. <strong>Should I use a bare or non-bare repository on the server?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Use a bare repository for collaboration. Bare repos are designed for pushes\/pulls and avoid working-tree conflicts when multiple users interact with the same project over SSH.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765963107463\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"3-how-do-i-give-multiple-users-write-access-safely\">3. <strong>How do I give multiple users write access safely?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Create a shared Unix group, add users to it, chown repos to git:group, enable g+ws on repo directories, and set core.sharedRepository to group. This keeps permissions consistent and avoids permission denied errors.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765963123802\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"4-can-i-disable-passwords-and-use-only-ssh-keys\">4. <strong>Can I disable passwords and use only SSH keys?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes, Set PasswordAuthentication no and PubkeyAuthentication yes in sshd_config, then reload sshd. This is the recommended hardening step for Git servers exposed to the internet.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765963140962\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"5-what-if-i-want-a-web-ui-for-issues-and-pull-requests\">5. <strong>What if I want a web UI for issues and pull requests?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Install Gitea for a lightweight UI with issues and PRs, or GitLab for a full DevOps stack. If you only need private code hosting, the SSH-only method in this guide is sufficient and low-maintenance.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>To create Git on a Linux server, install Git, add a dedicated \u201cgit\u201d user, initialize a bare repository for collaboration, [&hellip;]<\/p>\n","protected":false},"author":21,"featured_media":16350,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[350],"tags":[],"class_list":["post-14238","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-knowledgebase"],"acf":[],"featured_image_src":"https:\/\/www.youstable.com\/blog\/wp-content\/uploads\/2025\/12\/How-to-Create-Git-on-Linux-Server-for-CICD-Workflows.jpg","author_info":{"display_name":"Sanjeet Chauhan","author_link":"https:\/\/www.youstable.com\/blog\/author\/sanjeet"},"_links":{"self":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts\/14238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/comments?post=14238"}],"version-history":[{"count":6,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts\/14238\/revisions"}],"predecessor-version":[{"id":16352,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts\/14238\/revisions\/16352"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/media\/16350"}],"wp:attachment":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/media?parent=14238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/categories?post=14238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/tags?post=14238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}