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