{"id":678,"date":"2026-07-20T08:12:24","date_gmt":"2026-07-20T08:12:24","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=678"},"modified":"2026-08-05T22:18:17","modified_gmt":"2026-08-05T22:18:17","slug":"vps-git-server-setup-guide-technical-2026","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/","title":{"rendered":"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Git is a peer-to-peer protocol at heart \u2014 a remote is just another clone of the repository \u2014 which means a VPS can host your code with nothing more than SSH and <code>git init --bare<\/code>. When you want a web UI, issue tracking, and per-user permissions, Gitea fills that role in a single ~100 MB Go binary that idles under 100 MB of RAM, while GitLab CE recommends 4 GB or more for the same job.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Memory is the deciding factor: a bare-repo setup runs comfortably on a 1 GB plan, while Gitea is happy on 2 GB. <a href=\"https:\/\/virtualserversvps.com\/#providers\">see the full specs on our VPS comparison table<\/a> and match RAM to the option you choose below.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bare Repositories vs Git Hosting Software<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Bare repository over SSH: zero daemons, zero web surface, works with any Git client. Ideal for 1\u20135 developers who live in the terminal.<\/li><li>Gitea: lightweight web UI, pull requests, webhooks, 2FA, and CI integrations in one binary. The sweet spot for small teams.<\/li><li>GitLab CE: heavyweight pipelines and compliance features, but demands 4 GB+ RAM \u2014 usually overkill on a small VPS.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Option 1: A Bare Repository over SSH<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code># on the VPS\nmkdir -p \/srv\/git && cd \/srv\/git\ngit init --bare myproject.git<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># on your laptop\ngit remote add origin ssh:\/\/git@your.vps.ip:22\/srv\/git\/myproject.git\ngit push -u origin main<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create a dedicated <code>git<\/code> system user, put the repositories under <code>\/srv\/git<\/code>, and every developer authenticates with their own SSH key. No extra software, no web panel to patch \u2014 just Git and sshd.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bare repositories are the raw material of Git hosting: a bare repo has no working tree, only the <code>.git<\/code> internals, which makes it safe to push to. Give each repo a <code>.git<\/code> suffix by convention (<code>myproject.git<\/code>) so tooling and humans both recognize it as a remote. Set <code>git config receive.denyNonFastForwards true<\/code> per repo if you want to force the team to use merge requests instead of force-pushing over each other.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Option 2: Installing Gitea<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>useradd -r -m -s \/bin\/bash git\ncurl -L -o \/usr\/local\/bin\/gitea https:\/\/dl.gitea.com\/gitea\/1.22\/gitea-1.22-linux-amd64\nchmod +x \/usr\/local\/bin\/gitea\nmkdir -p \/var\/lib\/gitea\/{custom,data,log} && chown -R git:git \/var\/lib\/gitea<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Gitea runs as a single binary under the <code>git<\/code> user. Point it at a SQLite database for small teams or MariaDB\/PostgreSQL past a few dozen users, and put it behind Nginx with TLS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The install script downloads the binary, but verify the checksum from the official release page before running it \u2014 you are downloading code that will hold your entire source tree. After first launch, the web installer asks for the database, site name, and the <code>git<\/code> user; keep the install path under <code>\/var\/lib\/gitea<\/code> so upgrades are just a binary swap plus <code>gitea migrate<\/code>.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Run Gitea on port 3000 internally and let Nginx terminate TLS \u2014 do not expose the raw port to the internet.<\/li><li>Enable the built-in 2FA for every account; the web UI is now the attack surface, not just SSH.<\/li><li>Configure a backup of the Gitea data directory (<code>gitea dump<\/code>) on a schedule \u2014 the repositories are only as safe as your backup pipeline.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">SSH Keys: Locking Down Git Access<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir -p \/home\/git\/.ssh && chmod 700 \/home\/git\/.ssh\necho \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... dev@laptop\" >> \/home\/git\/.ssh\/authorized_keys\nchmod 600 \/home\/git\/.ssh\/authorized_keys<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>Use ed25519 keys (<code>ssh-keygen -t ed25519<\/code>) \u2014 they are faster and shorter than RSA 2048.<\/li><li>Add one key per developer and revoke access by deleting that line \u2014 no shared passwords anywhere.<\/li><li>Disable password authentication in <code>sshd_config<\/code> so the only way in is a key.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For Gitea-managed repos, SSH keys are uploaded through the web UI and Gitea&#8217;s own SSH server (or its authorized_keys wrapper) handles them, which means you manage keys per-user in one place. For bare repos, keys live in <code>\/home\/git\/.ssh\/authorized_keys<\/code>. Either way, never let developers share an account \u2014 auditing who pushed what becomes impossible the moment two people share a key.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Add a <code>restrict<\/code> prefix to each key in <code>authorized_keys<\/code> (for example <code>restrict,command=\"\/usr\/bin\/git-shell -c \"$SSH_ORIGINAL_COMMAND\"\" ssh-ed25519 ...<\/code>) so a stolen key can only run Git commands, not an interactive shell on your VPS. This is the single most important hardening step for a self-hosted Git server \u2014 it turns a compromised laptop key into a non-event instead of full server access.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Repository Hooks for Automation<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# \/srv\/git\/myproject.git\/hooks\/post-receive\nGIT_WORK_TREE=\/var\/www\/myproject git checkout -f\ncd \/var\/www\/myproject && systemctl reload nginx php8.3-fpm 2>\/dev\/null || true<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>Make the hook executable with <code>chmod +x hooks\/post-receive<\/code>.<\/li><li>Hooks run as the <code>git<\/code> user, so grant that user write access to the deployment directory.<\/li><li>Test with an empty commit \u2014 a broken hook fails the push, which is exactly what you want to see locally.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">git gc and Disk Hygiene<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>git -C \/srv\/git\/myproject.git gc --auto\n# crontab -e (as the git user):\n0 3 * * 0 git -C \/srv\/git gc --auto && du -sh \/srv\/git\/*<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Repositories grow with every force-push and branch deletion; <code>git gc --auto<\/code> packs loose objects and prunes unreachable ones. Add the cron entry so housekeeping runs weekly without you remembering to do it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Watch disk usage on the VPS as the team grows: a few dozen active repositories with CI artifacts can consume several gigabytes. Set an alert at 80% utilization and migrate to a larger data volume before the disk fills \u2014 a full disk corrupts repositories in ways <code>git fsck<\/code> cannot always repair.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start with a bare repo and SSH keys \u2014 it is the most secure option by default because there is no web daemon to attack. If the team outgrows it, Gitea slots in without changing your remotes. When you provision the server, <a href=\"https:\/\/virtualserversvps.com\/#providers\">compare plans side by side on our comparison table<\/a> to right-size RAM and disk for your repository count.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">InterServer&#8217;s VPS plans include dedicated IPs and generous bandwidth \u2014 plenty for a self-hosted Git server with a team of any size. Check <a href=\"https:\/\/interserver.net\/vps?id=1067805&amp;sid=virtualserversvps\" rel=\"noreferrer noopener sponsored\" target=\"_blank\">InterServer VPS plans and pricing<\/a> to get your code off third-party hosts.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Git is a peer-to-peer protocol at heart \u2014 a remote is just another clone of the repository \u2014 which means a VPS can host your code with nothing more than&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":4,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-678","post","type-post","status-publish","format-standard","hentry","category-vps-guides-tutorials"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea - Virtual Servers VPS Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea\" \/>\n<meta property=\"og:description\" content=\"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-20T08:12:24+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-05T22:18:17+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/\",\"name\":\"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-07-20T08:12:24+00:00\",\"dateModified\":\"2026-08-05T22:18:17+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea - Virtual Servers VPS Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/","og_locale":"en_US","og_type":"article","og_title":"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea","og_description":"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea","og_url":"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-07-20T08:12:24+00:00","article_modified_time":"2026-08-05T22:18:17+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/","url":"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/","name":"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-07-20T08:12:24+00:00","dateModified":"2026-08-05T22:18:17+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/vps-git-server-setup-guide-technical-2026\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Self-Hosted Git Server on a VPS: Bare Repos, SSH Keys, and Gitea"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/678","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=678"}],"version-history":[{"count":2,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/678\/revisions"}],"predecessor-version":[{"id":805,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/678\/revisions\/805"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=678"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=678"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=678"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}