annotate mrjunejune/src/blog/wsl2-ssh/index.md @ 149:f41ac17926d2

[Config] Added ctags scripts and actual tags.
author June Park <parkjune1995@gmail.com>
date Sat, 10 Jan 2026 07:07:10 -0800
parents 65e5a5b89a4e
children 295ac2e5ec00
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
100
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
1
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
2 Sorry for going MIA. I hadn’t had any programming-related ideas worth sharing lately, and nothing I wrote in my free time felt ready. But recently, something sparked my interest.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
3
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
4 If you’ve worked at a FAANG company, chances are you’ve used *cloudtop*—essentially SSH’ing into a remote dev server because your laptop can’t realistically run a local version of Facebook or Gmail. Personally, I’ve grown to like the workflow. It gives me a consistent development environment no matter where I go, and it’s fast enough that I don’t run into many issues while coding.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
5
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
6 Also, I should probably use that $2K computer I bought... which has mostly been collecting dust.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
7
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
8 ## What am I trying to achieve?
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
9
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
10 I have a domain lying around from a startup idea where I planned to post free educational content and maybe get some coffee money out of it—because, yeah, I’m broke. Like most of my ideas, I never got around to finishing it. But now, I’m putting it to use for this setup.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
11
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
12 Here’s the plan:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
13
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
14 1. Point a DNS record to my public IP address.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
15 2. Forward traffic from my router (public IP) to my Windows machine (internal IP).
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
16 3. Use Windows `netsh` to proxy ports from Windows to my WSL2 instance.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
17 4. Set up an SSH server inside WSL2 (I'm using Debian).
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
18
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
19 You might wonder why I’m using WSL2 instead of just working directly from Windows. It’s simple: I’m a creature of habit. I can’t get used to PowerShell or all the Windows-specific shortcuts. My WSL2 instance runs Debian, but this tutorial should work on most Linux distros with minor adjustments.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
20
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
21 The list above flows from DNS all the way to the WSL2 SSH server. Let’s now go step-by-step and verify each part.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
22
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
23 ## Setting up SSH in WSL2
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
24
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
25 We’ll use `openssh-server`. Yes, you could write your own SSH server, but let’s not kid ourselves—just use something battle-tested.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
26
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
27 ```bash
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
28 sudo apt update
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
29 sudo apt install openssh-server
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
30 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
31
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
32 If you’re not using Debian, substitute with your distro’s package manager.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
33
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
34 Now check if the SSH service is running:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
35
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
36 ```bash
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
37 sudo systemctl status ssh
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
38 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
39
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
40 If it's not running:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
41
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
42 ```bash
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
43 sudo systemctl start ssh
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
44 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
45
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
46 To start SSH automatically on boot:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
47
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
48 ```bash
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
49 sudo systemctl enable ssh
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
50 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
51
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
52 Now SSH is running on your WSL2 instance. But you can’t access it from outside without configuring Windows to forward traffic to WSL2.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
53
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
54 **warning**: You should be careful when you are setting up SSH. Try to use PublicKeyAuthentication over password as you will get many robot visitor trying to login as a root ^_^
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
55
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
56 ## Setting up Portproxy on Windows
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
57
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
58 I am picking port number 2222 on my window machine, but you can pick whatever port number that is avaialble. To have your Windows host forward to port 22(default SSH port number) on your WSL2 instance (replace the IP with your actual WSL2 IP):
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
59
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
60 If you don't know your WSL2 IP address, you can easily see this by using ipconfig in powershell.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
61
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
62 ```powershell
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
63 netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=22 connectaddress=<WSL2-IP>
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
64 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
65
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
66 To view current portproxy rules:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
67
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
68 ```powershell
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
69 netsh interface portproxy show all
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
70 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
71
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
72 If you need to remove it (only do this if you know that port is not being used)
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
73
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
74 ```powershell
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
75 netsh interface portproxy delete v4tov4 listenport=2222 listenaddress=0.0.0.0
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
76 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
77
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
78 You should now be able to SSH into your WSL2 instance from your Windows machine:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
79
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
80 ```bash
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
81 ssh -p 2222 <your-wsl-username>@localhost
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
82 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
83
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
84 Or from another device on the same network:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
85
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
86 ```bash
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
87 ssh -p 2222 <your-wsl-username>@<Windows-internal-IP>
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
88 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
89
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
90 ## Setting Up Port Forwarding on Your Router
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
91
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
92 To make it publicly accessible, forward port 2222 on your router to the internal IP of your Windows machine.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
93
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
94 If you're using Xfinity, you can do this from the Xfinity app. Otherwise, you can often log into your router at `http://10.0.0.1` or `http://192.168.0.1`. Default credentials are often:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
95
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
96 - **Username**: `admin`
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
97 - **Password**: `password` (varies by model/provider, so check your router label or ISP’s documentation)
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
98
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
99 ## Pointing a Domain to Your Public IP
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
100
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
101 Once your SSH server is reachable via your public IP, buy a domain from Namecheap or another registrar and set up an A record pointing to that IP.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
102
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
103 However, most residential IPs are dynamic, so they can change. To deal with this, use a dynamic DNS (DDNS) solution.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
104
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
105 I use **Cloudflare** with a small PowerShell script that updates the A record periodically:
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
106
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
107 ```powershell
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
108 $zoneId = "<your-zone-id>"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
109 $recordId = "<your-record-id>"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
110 $apiToken = "<your-api-token>"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
111 $recordName = "<your-domain>"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
112 $currentIP = Invoke-RestMethod -Uri "https://api.ipify.org"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
113
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
114 $headers = @{
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
115 "Authorization" = "Bearer $apiToken"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
116 "Content-Type" = "application/json"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
117 }
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
118
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
119 $body = @{
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
120 type = "A"
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
121 name = $recordName
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
122 content = $currentIP
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
123 ttl = 120
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
124 proxied = $false
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
125 } | ConvertTo-Json
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
126
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
127 Invoke-RestMethod -Method PUT `
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
128 -Uri "https://api.cloudflare.com/client/v4/zones/$zoneId/dns_records/$recordId" `
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
129 -Headers $headers `
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
130 -Body $body
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
131 ```
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
132
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
133 You can schedule this with Windows Task Scheduler to run every 10–15 minutes.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
134
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
135 ## That’s It
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
136
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
137 TADA! You now have your own *Cloudtop* setup—using your personal machine. It only costs electricity (which might be more than a free AWS tier, lol), but it’s yours.
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
138
65e5a5b89a4e [Seobeo] Migrated everything to this page.
June Park <parkjune1995@gmail.com>
parents:
diff changeset
139 In the next post, I’ll show you how to use the exact same steps to host a full-featured server, including self-hosting your own GitHub-like instance.