2020-06-23-setting-up-a-personal-git-server.html (12866B) - raw
1 <!-- title: Setting up a personal Git server --> 2 <!-- slug: setting-up-a-personal-git-server --> 3 <!-- categories: Decentralization, Personal domain, Self-hosting --> 4 <!-- date: 2020-06-23T16:10:00Z --> 5 <!-- lastmod: 2020-07-24T15:17:00Z --> 6 7 <p> 8 Running a personal Git server is something that has been on my mind for quite a long time. One of 9 the most popular solutions I have seen around is <a href="https://gitea.io">Gitea</a>. A couple of 10 months ago, when trying out different things, I decided to run Gitea locally to see how easy it 11 would be to implement on my server. It turns out pretty easy, especially if you are using Docker. 12 However, my server doesn't run Docker as of now and it also felt like customizing it would be hard 13 (for example, getting rid of the username in the URLs). Gitea looks like a very good solution for 14 self-hosting Git (and the sites look very nice!), but in my case, it felt like using a 15 sledgehammer to crack a nut. I figured most self-hosted Git solutions would turn out to be a bit 16 too much for my use case, so I decided to look into hosting Git without any other program.</p> 17 <!-- /p --> 18 19 <p> 20 I had experience setting up a bare-bones Git server only usable through SSH, so I looked up how to 21 create a website with the <code>bare</code> repositories. It turns out there's even a built-in 22 option<sup id="fnref1"><a href="#fn1">1</a></sup>! Other programs have more or less similar looks, 23 but I decided to check if there was any way to have a static generator for the webpage—the fewer 24 things running on my server the better! And there is one (that I found): 25 <a href="https://codemadness.org/stagit.html">stagit</a>. It is very simple, and it does the job. 26 On top of that, the program is super small, which makes it very easy to modify it if needed<sup 27 id="fnref2"><a href="#fn2">2</a></sup>. I gave it a try and it worked nicely. I decided that it 28 was a good solution for my use case, therefore having a vanilla Git server would work, so I 29 started building it<sup id="fnref3"><a href="#fn3">3</a></sup>.</p> 30 <!-- /p --> 31 32 <p>Let's see how to set it up!</p> 33 34 <h2>Setting up a Git server</h2> 35 36 <p> 37 What will happen is we'll have <code>bare</code> repositories on our server which we'll then 38 clone/fetch/push to using SSH<sup id="fnref4"><a href="#fn4">4</a></sup>. We'll put these 39 repositories on the <code>/srv/git</code> directory—because I keep everything served from my 40 server on <code>/srv</code>—but any location will work. To keep Git separated from the root user, 41 we'll create a new <code>git</code> user, with <code>/srv/git</code> as its home folder, this way, 42 the remote address will be <code>git@domain:repo.git</code> (no need for an absolute address). 43 Let's do that:</p> 44 <!-- /p --> 45 46 <pre><code><!-- 47 -->useradd -d /srv/git git 48 <!-- -->mkdir /srv/git 49 <!-- -->chown git /srv/git</code></pre> 50 51 <p>Now, let's create the folder for the SSH configuration where we'll add our public key.</p> 52 53 <pre><code><!-- 54 -->su git 55 <!-- -->cd 56 <!-- -->mkdir .ssh && chmod 700 .ssh 57 <!-- -->touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys</code></pre> 58 59 <p> 60 We could stop here, adding our key to the file (you can also use <code>ssh-copy-id</code>), but we 61 can take a couple more steps to isolate the user from the server, useful especially if you want to 62 share access to the git user with someone else.</p> 63 <!-- /p --> 64 65 <p> 66 To do that, we'll use the <code>git-shell</code> shell, which will only run scripts found on 67 <code>~/git-shell-commands</code>.<sup id="fnref5"><a href="#fn5">5</a></sup></p> 68 <!-- /p --> 69 70 <pre><code>chsh git -s $(which git-shell)</code></pre> 71 72 <p> 73 Now if you add someone else's public SSH key to the server, they won't be able to run any command. 74 If you want to allow some commands, create scripts on <code>~/git-shell-commands</code>. For 75 example, I have scripts to initiate repositories, add SSH keys and other useful commands, you can 76 see them all <a href="https://git.oscarbenedito.com/git-shell-commands/">here</a>.</p> 77 <!-- /p --> 78 79 <h2>Making repositories public</h2> 80 81 <p> 82 Now we can pull and push to our repository, and we can share access to them by adding SSH keys (or 83 sharing the password if you use one). However, we might want to have public repositories that 84 people should be able to clone without the need to have access to all of them. To do so, we'll use 85 the Git Daemon (that uses the Git Protocol). All we have to do is run the following command (and 86 keep it running, I recommend running a systemd service if you use systemd, there is an example 87 <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-Git-Daemon">here</a>).</p> 88 <!-- /p --> 89 90 <pre><code>git daemon --reuseaddr --base-path=/srv/git/ /srv/git/</code></pre> 91 92 <p> 93 This daemon will only serve repositories that have a file named <code>git-daemon-export-ok</code> 94 in them, so if you want to make a certain repository public, all you have to do is run:</p> 95 <!-- /p --> 96 97 <pre><code>touch /srv/git/repo.git/git-daemon-export-ok</code></pre> 98 99 <p> 100 Remove that file to make it private again. The cloning address will be 101 <code>git://domain/repo.git</code> and you can't push to that address. You can also serve 102 repositories through HTTP which will allow you to have fine-grained control over who can access 103 which repositories, look it up if you are interested.</p> 104 <!-- /p --> 105 106 <h2>Making a website</h2> 107 108 <p> 109 Now we can host private and public repositories, but we may want to share the public ones easily. 110 A website is a good way to allow people to quickly see your repositories without the need to clone 111 every single one of them. We'll use stagit to create the HTML files which we'll host as a static 112 site. First of all, let's install stagit:</p> 113 <!-- /p --> 114 115 <pre><code><!-- 116 -->git clone git://git.codemadness.org/stagit 117 <!-- -->cd stagit 118 <!-- -->sudo make install</code></pre> 119 120 <p>To create a website for a repository run</p> 121 122 <pre><code>stagit /path/to/repo.git</code></pre> 123 124 <p>from the directory where you want the site built. To create an index file with all your repositories simply run</p> 125 126 <pre><code>stagit-index /path/to/repo-1.git /path/to/repo-2.git > index.html</code></pre> 127 128 <p> 129 with the path to all your repositories. Make sure you have the correct information on the 130 <code>owner</code>, <code>description</code> and <code>url</code> files so that stagit uses them 131 when creating the HTML.</p> 132 <!-- /p --> 133 134 <p> 135 Having to do this is every time you update your repositories is not a reasonable solution, but we 136 can set up a <code>post-receive</code> hook to do it for us. There are examples of scripts to use 137 as an initial creation script for the whole site and a post-receive hook on stagit's source code 138 repository. I have changed those scripts a bit to only process public repositories (the ones with 139 the <code>git-daemon-export-ok</code> file) as well a modified the source a bit. You can find the 140 changes on my <a href="https://git.oscarbenedito.com/stagit/">stagit fork</a>.</p> 141 <!-- /p --> 142 143 <h2>Pros of this setup</h2> 144 145 <p> 146 I have only been using this server for a couple of days, but I have also been setting up a bunch 147 of <a href="https://suckless.org">suckless</a> tools<sup id="fnref6"><a href="#fn6">6</a></sup>, 148 so I have been using Git a lot. One of the best things is that setting up repositories is very 149 easy. No need to open a browser, log in to GitHub and go through the process of creating a new 150 repository<sup id="fnref7"><a href="#fn7">7</a></sup>. I just run</p> 151 <!-- /p --> 152 153 <pre><code><!-- 154 -->ssh git@oscarbenedito.com 155 <!-- -->init reponame</code></pre> 156 157 <p> 158 Done! I can also set it up as public by running a script I have on my <code>git-shell</code> and 159 change the description/owner/url just as easily.</p> 160 <!-- /p --> 161 162 <p> 163 Another thing I have noticed is that Git's clone/fetch/push commands are significantly faster on 164 this server than GitLab or GitHub. However, I don't know compared to a self-hosted instance of 165 Gitea or <a href="https://gogs.io">Gogs</a>.</p> 166 <!-- /p --> 167 168 <h2>Cons of this setup</h2> 169 170 <p> 171 One thing that might be missed by this setup is the ability to download a tarball with the code 172 from a certain commit, browse the code as it was in a given commit, see git blame, etc. This could 173 be solved by using another tool for the website part—such as <a 174 href="https://git.zx2c4.com/cgit/about/">cgit</a>—so if I ever want to do that, it shouldn't be hard.</p> 175 <!-- /p --> 176 177 <p> 178 Another thing is how the website looks. Other self-hosting solutions like Gogs, Gitea, GitLab... 179 look a lot nicer than stagit. However this isn't a priority right now and I appreciate the ability 180 to have full control over how the server works—and it has been very interesting to learn about it 181 all. Once again this is something to do with the website, and not the repository hosting 182 itself.</p> 183 <!-- /p --> 184 185 <h2>Final comments</h2> 186 187 <p> 188 It has been fun to set everything up and realize how easy it is to self-host Git without the need 189 for other software. On top of that, I can now share my repositories from my domain, which means it 190 is easier to stop using other hosting services if I stop wanting to accept their policies, similar 191 to having email on my domain.</p> 192 <!-- /p --> 193 194 <p> 195 For now, I will still have my public repositories hosted on GitLab and GitHub, as I don't mind it 196 and some people might be more used to those UIs. They also act as a backup in case something 197 happens to my server or if I want to edit my repositories from a computer without SSH access to my 198 server.</p> 199 <!-- /p --> 200 201 <p> 202 Finally, I have talked about some software that will allow you to self-host Git, but I don't want 203 to end this post without mentioning <a href="https://sourcehut.org/">sourcehut</a>. I find it a 204 very good solution because everyone can contribute to your projects without the need to create yet 205 another account. Everything is done through email, which is nicely supported by Git (learn more 206 <a href="https://git-send-email.io/">here</a>).</p> 207 <!-- /p --> 208 209 <p> 210 I almost forgot! If you want to check out my Git website or clone some repositories from my 211 domain, you can find all of that here: <a href="https://git.oscarbenedito.com">https://git.oscarbenedito.com</a>.</p> 212 <!-- /p --> 213 214 <p> 215 <em>Edit</em>: The post originally said that creating a new repository on GitLab was a long 216 process. However, you can just push to a new remote address and GitLab will automatically create 217 the new repository.</p> 218 <!-- /p --> 219 220 <!-- footnotes --> 221 <hr /> 222 223 <ol> 224 <li id="fn1"> 225 Run <code>git instaweb</code> from a Git repository to try it. <a href="#fnref1" title="Jump 226 back to footnote 1 in the text">↩</a></li> 227 <!-- /li --> 228 <li id="fn2"> 229 I haven't modified the source code much yet, but I have some ideas in mind. <a href="#fnref2" 230 title="Jump back to footnote 2 in the text">↩</a></li> 231 <!-- /li --> 232 <li id="fn3"> 233 Funnily enough, I had already set up a Git server there for a couple of repositories containing 234 a lot of personal information to avoid hosting them on GitLab or GitHub. I completely forgot 235 about it. I deleted it and started from scratch, as I wanted to document the process on my 236 personal wiki. <a href="#fnref3" title="Jump back to footnote 3 in the text">↩</a></li> 237 <!-- /li --> 238 <li id="fn4"> 239 Bare repositories are simply a folder with the files of the <code>.git</code> directory of a 240 repository. Indeed, you can clone a repository from your computer running <code>git clone 241 /path/to/repo/.git cloned-repo</code>. This directory contains all the information of the 242 repository, that is why Git is distributed. <a href="#fnref4" title="Jump back to footnote 4 in 243 the text">↩</a></li> 244 <!-- /li --> 245 <li id="fn5"> 246 If the <code>chsh</code> command doesn't work, make sure <code>git-shell</code> is in 247 <code>/etc/shells</code>, if not, add it manually. <a href="#fnref5" title="Jump back to 248 footnote 5 in the text">↩</a></li> 249 <!-- /li --> 250 <li id="fn6"> 251 Fun fact: after setting everything up I realized that suckless uses stagit to show their 252 repositories, indeed the author of stagit is the current maintainer of some suckless projects 253 like <a href="https://st.suckless.org/">st</a> and <a 254 href="https://tools.suckless.org/dmenu/">dmenu</a>. <a href="#fnref6" title="Jump 255 back to footnote 6 in the text">↩</a></li> 256 <!-- /li --> 257 <li id="fn7"> 258 I originally thought this was also the case for GitLab, however, you can push a new repository 259 to a new remote address and GitLab will automatically create it. <a href="#fnref7" title="Jump 260 back to footnote 7 in the text">↩</a></li> 261 <!-- /li --> 262 </ol>