<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>StdOut</title>
  <subtitle>Writing thoughts to standard output...</subtitle>
  <link href="https://blog.kauriss.site/en/" />
  <link href="https://blog.kauriss.site/en/atom.xml" rel="self" />
  <id>https://blog.kauriss.site/en/</id>
  <updated>2026-04-25T00:00:00.000Z</updated>
  
  <entry>
    <title>Cloudflare Workers Optimization Tutorial</title>
    <link href="https://blog.kauriss.site/en/2026/04/25/Cloudflare-Workers-%E4%BC%98%E9%80%89%E6%95%99%E7%A8%8B/" />
    <id>https://blog.kauriss.site/en/2026/04/25/Cloudflare-Workers-%E4%BC%98%E9%80%89%E6%95%99%E7%A8%8B/</id>
    <updated>2026-04-25T00:00:00.000Z</updated>
    <summary>Use custom domains, DNS only CNAMEs, and Workers routing to direct Workers projects through optimized Cloudflare IPs.</summary>
    <content type="html"><![CDATA[<p>Cloudflare Workers assigns a <code>workers.dev</code> domain by default.</p>
<p>It works, but accessing it from mainland China can sometimes be unstable. Some projects use <code>workers.dev</code> directly as their entry point, making latency and availability a matter of luck.</p>
<p>Here is a common workaround: instead of using the default <code>workers.dev</code>, use your own domain, CNAME it to a Cloudflare optimized domain, and finally catch the requests using Workers routing.</p>
<p>Simply put:</p>
<pre><code class="language-text">用户 -&gt; 你的域名 -&gt; 优选域名对应的 Cloudflare 节点 -&gt; Workers 路由 -&gt; 你的 Worker</code></pre><p>This isn&#39;t magic acceleration, nor does it guarantee the fastest speeds forever. It just changes the entry point to a more suitable Cloudflare node.</p>
<h2 id="when-to-use-this">When to Use This</h2><p>This guide is best suited for the following situations:</p>
<ul>
<li>You already have a functioning Worker.</li>
<li>The default <code>workers.dev</code> entry point is unstable in your network.</li>
<li>You have your own domain, and it is already hosted on Cloudflare.</li>
<li>You want to specify an optimized entry point yourself, rather than using Cloudflare&#39;s automatically generated binding method.</li>
</ul>
<p>If you just want to bind a regular domain to your Worker without messing with optimized domains, using Custom Domains directly is much simpler.</p>
<h2 id="conclusion-first">Conclusion First</h2><p>This guide uses Workers Routes, not Custom Domains.</p>
<p>The reason is simple:</p>
<ul>
<li>Custom Domains will cause Cloudflare to automatically create DNS records.</li>
<li>However, here we need to manually CNAME the domain to an optimized domain.</li>
<li>Therefore, a Workers Route like <code>domain/*</code> is much more suitable.</li>
</ul>
<p>If you are just deploying a Worker normally without setting up an optimized domain, using Custom Domains is easier.</p>
<p>If you want to specify your own optimized entry point, using routes is more appropriate.</p>
<h2 id="prerequisites">Prerequisites</h2><p>You will need the following:</p>
<table>
<thead>
<tr>
<th align="left">Item</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Cloudflare account</td>
<td align="left">To deploy the Worker</td>
</tr>
<tr>
<td align="left">A domain hosted on Cloudflare</td>
<td align="left">e.g., <code>example.com</code></td>
</tr>
<tr>
<td align="left">A deployed Worker</td>
<td align="left">Ensure the Worker itself is accessible first</td>
</tr>
<tr>
<td align="left">An optimized domain</td>
<td align="left">e.g., a Cloudflare optimized domain maintained by others, or one you tested yourself</td>
</tr>
</tbody></table>
<p>In our examples, we assume:</p>
<pre><code class="language-text">Worker 名称：my-worker
你的域名：wk.example.com
优选域名：cloudflare.example.net</code></pre><p>Replace these with your own when following along.</p>
<h2 id="final-result">Final Result</h2><p>Once finished, you will access your own domain:</p>
<pre><code class="language-text">https://wk.example.com</code></pre><p>The request will first go to the Cloudflare node corresponding to your optimized domain, and then be handed over to the target Worker by the Workers Route. Normal visits, API paths, and static resource paths should all be caught by the same Worker.</p>
<h2 id="step-1-verify-the-worker-is-functioning-normally">Step 1: Verify the Worker is Functioning Normally</h2><p>Don&#39;t rush to change your DNS just yet.</p>
<p>Open the default Worker address:</p>
<pre><code class="language-text">https://my-worker.&lt;你的 workers.dev 子域&gt;.workers.dev</code></pre><p>Proceed only if it returns content normally.</p>
<p>If the default address doesn&#39;t even load, fix the Worker first. Otherwise, adding DNS and routing later will only make things more confusing.</p>
<p>You can also test it via command line:</p>
<pre><code class="language-bash">curl -I https://my-worker.&lt;你的 workers.dev 子域&gt;.workers.dev</code></pre><p>As long as you get a normal HTTP status code, you&#39;re good.</p>
<h2 id="step-2-add-dns-records">Step 2: Add DNS Records</h2><p>Go to the Cloudflare dashboard and select your domain <code>example.com</code>.</p>
<p>Add a DNS record:</p>
<table>
<thead>
<tr>
<th align="left">Type</th>
<th align="left">Name</th>
<th align="left">Target</th>
<th align="left">Proxy status</th>
</tr>
</thead>
<tbody><tr>
<td align="left">CNAME</td>
<td align="left"><code>wk</code></td>
<td align="left"><code>cloudflare.example.net</code></td>
<td align="left">DNS only</td>
</tr>
</tbody></table>
<p>The key here is to select <strong>DNS only</strong> (the gray cloud) for the proxy status.</p>
<p>Do not enable the proxy.</p>
<p>If you enable the proxy, Cloudflare will process it using its standard proxy logic, and the entry point may not be the optimized domain you want. Many people make a mistake here.</p>
<p>After adding it, your domain should be:</p>
<pre><code class="language-text">wk.example.com</code></pre><h2 id="step-3-add-workers-route">Step 3: Add Workers Route</h2><p>Go to the Cloudflare dashboard:</p>
<pre><code class="language-text">Workers 和 Pages -&gt; 你的 Worker -&gt; 设置 -&gt; 域和路由</code></pre><p>Add a route:</p>
<pre><code class="language-text">wk.example.com/*</code></pre><p>Then select your project for the Worker, such as <code>my-worker</code>.</p>
<p>The <code>/*</code> here cannot be omitted.</p>
<p>If you don&#39;t add it, the route will only match the root path, and many APIs, static resources, and sub-paths will just break.</p>
<h2 id="step-4-test-access">Step 4: Test Access</h2><p>Open in your browser:</p>
<pre><code class="language-text">https://wk.example.com</code></pre><p>Test via command line:</p>
<pre><code class="language-bash">curl -I https://wk.example.com</code></pre><p>If your Worker has API paths, test those too:</p>
<pre><code class="language-bash">curl -I https://wk.example.com/api</code></pre><p>Don&#39;t just test the homepage. Many projects load fine on the homepage, but API paths aren&#39;t caught by the route, which you only discover later.</p>
<h2 id="how-to-verify-its-working">How to Verify It&#39;s Working</h2><p>First, check the DNS:</p>
<pre><code class="language-bash">nslookup wk.example.com</code></pre><p>Or:</p>
<pre><code class="language-bash">dig wk.example.com</code></pre><p>Normally, you should see it eventually pointing to the optimized domain you set.</p>
<p>Then test HTTPS:</p>
<pre><code class="language-bash">curl -I https://wk.example.com</code></pre><p>If it returns the response from your Worker, it means the route is also working.</p>
<h2 id="frequently-asked-questions">Frequently Asked Questions</h2><h3 id="1.-returning-a-404-error">1. Returning a 404 Error</h3><p>This is most likely because the route is not configured correctly.</p>
<p>Check:</p>
<pre><code class="language-text">wk.example.com/*</code></pre><p>Verify the domain name, the asterisk, and the slash.</p>
<h3 id="2.-dns-resolves-but-worker-doesnt-respond">2. DNS Resolves, but Worker Doesn&#39;t Respond</h3><p>DNS only brings the request to the Cloudflare node.</p>
<p>Whether the Worker executes depends on the Workers Route. Go to &quot;Domains &amp; Routes&quot; to check if the route is bound to the correct Worker.</p>
<h3 id="3.-https-certificate-error">3. HTTPS Certificate Error</h3><p>Wait a little while first.</p>
<p>If it still doesn&#39;t work, check if the domain is properly hosted in Cloudflare and if the DNS records are typed correctly.</p>
<p>Also, don&#39;t randomly use domains that don&#39;t belong to you. That&#39;s not how certificates and routing work.</p>
<h3 id="4.-optimized-domain-suddenly-slows-down">4. Optimized Domain Suddenly Slows Down</h3><p>This is normal.</p>
<p>&quot;Optimization&quot; is not an absolute truth; it just tested well at the moment. Networks change, and nodes change too.</p>
<p>There are only two solutions:</p>
<ul>
<li>Switch to a different optimized domain.</li>
<li>Run speed tests yourself regularly.</li>
</ul>
<h3 id="5.-is-the-free-quota-enough">5. Is the Free Quota Enough?</h3><p>It is generally enough for regular personal projects.</p>
<p>The Cloudflare Workers free plan has daily request limits and CPU time limits. Lightweight APIs, redirect pages, and small tools usually have no issues.</p>
<p>But if you are using it for high-traffic downloads or reverse proxying a bunch of things, don&#39;t expect to free-ride indefinitely. You&#39;ll hit the limits sooner or later.</p>
<h2 id="choosing-between-routes-and-custom-domains">Choosing Between Routes and Custom Domains</h2><p>Keep it simple:</p>
<table>
<thead>
<tr>
<th align="left">Scenario</th>
<th align="left">Which to choose</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Normally binding your own domain</td>
<td align="left">Custom Domain</td>
</tr>
<tr>
<td align="left">Needing to manually CNAME to an optimized domain</td>
<td align="left">Route</td>
</tr>
<tr>
<td align="left">Worker is the entry point for a full app</td>
<td align="left">Custom Domain</td>
</tr>
<tr>
<td align="left">Want to preserve your own DNS pointing logic</td>
<td align="left">Route</td>
</tr>
</tbody></table>
<p>I&#39;m using routing here not because it&#39;s more advanced, but simply because it fits the &quot;optimized domain&quot; approach.</p>
<h2 id="a-complete-example">A Complete Example</h2><p>Assuming:</p>
<pre><code class="language-text">Worker：my-worker
域名：example.com
入口：wk.example.com
优选域名：cloudflare.example.net</code></pre><p>DNS:</p>
<pre><code class="language-text">CNAME  wk  cloudflare.example.net  仅 DNS 解析</code></pre><p>Workers Route:</p>
<pre><code class="language-text">wk.example.com/*</code></pre><p>Access:</p>
<pre><code class="language-text">https://wk.example.com</code></pre><p>If it returns the Worker content, you&#39;re done.</p>
<h2 id="final-thoughts">Final Thoughts</h2><p>This solution is suitable when you already have a Worker project but the default entry point access is unstable.</p>
<p>Don&#39;t overhype it. An optimized domain just changes the entry point; it won&#39;t make a poorly built project fast, nor will it fix code issues within the Worker itself.</p>
<p>Make sure the Worker works normally first, then change the DNS, and then bind the route. Following this order prevents things from getting messy.</p>
<h2 id="references">References</h2><ul>
<li><a href="https://developers.cloudflare.com/workers/configuration/routing/" target="_blank" rel="noopener noreferrer">Cloudflare Workers Routing</a></li>
<li><a href="https://developers.cloudflare.com/workers/configuration/routing/custom-domains/" target="_blank" rel="noopener noreferrer">Cloudflare Workers Custom Domains</a></li>
<li><a href="https://developers.cloudflare.com/workers/platform/limits/" target="_blank" rel="noopener noreferrer">Cloudflare Workers Limits</a></li>
</ul>
]]></content>
  </entry>
  <entry>
    <title>wg-manager WireGuard Management Script</title>
    <link href="https://blog.kauriss.site/en/2026/04/25/wg-manager-WireGuard-%E7%AE%A1%E7%90%86%E8%84%9A%E6%9C%AC/" />
    <id>https://blog.kauriss.site/en/2026/04/25/wg-manager-WireGuard-%E7%AE%A1%E7%90%86%E8%84%9A%E6%9C%AC/</id>
    <updated>2026-04-25T00:00:00.000Z</updated>
    <summary>A practical WireGuard management script supporting menu mode, adding/removing clients, exporting QR codes, checking status, and reloading configurations.</summary>
    <content type="html"><![CDATA[<p>WireGuard itself is not difficult.</p>
<p>What&#39;s really troublesome are the trivial tasks that follow: adding a phone, deleting an old device, generating a QR code, modifying configurations, reloading the service, and checking if peers have handshaked.</p>
<p>Manually editing <code>wg0.conf</code> is doable, but it gets annoying if you do it often. Moreover, if you accidentally mistype a private key, public key, or AllowedIPs, you have to go back and troubleshoot all over again.</p>
<p>So I wrote <code>wg-manager.sh</code>. The goal is simple: wrap up common WireGuard operations so you don&#39;t have to type them from scratch every time.</p>
<p><img src="/images/WireGuard-Logo.svg" alt=""></p>
<h2 id="applicable-scenarios">Applicable Scenarios</h2><p>This script is suitable for a small WireGuard environment on a personal VPS:</p>
<ul>
<li>Want to quickly add or remove clients.</li>
<li>Want to generate a QR code directly in the terminal to scan with a phone.</li>
<li>Don&#39;t want to manually edit <code>wg0.conf</code> every time.</li>
<li>Want to use a menu to check the status, reload the configuration, and export client files.</li>
</ul>
<p>If you already have a complex environment with policy routing, multiple network interfaces, and multiple exits, this script might not be suitable for taking over directly.</p>
<h2 id="prerequisites">Prerequisites</h2><p>It&#39;s written by default for common server environments like Debian / Ubuntu. Before using it, you need at least:</p>
<ul>
<li>A Linux server with root privileges.</li>
<li>A system that can normally install WireGuard-related packages.</li>
<li>A firewall or cloud provider security group that allows the UDP port used by WireGuard.</li>
<li>To roughly know the network segment you want to assign to your clients.</li>
</ul>
<h2 id="final-result">Final Result</h2><p>In the end, you can just run:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh</code></pre><p>Then use the menu to add/remove clients, view QR codes, check service status, and reload the configuration. You don&#39;t have to repeatedly look up commands for common operations.</p>
<h2 id="what-this-script-does">What This Script Does</h2><p><code>wg-manager</code> is a Bash script mainly used to manage WireGuard servers and clients.</p>
<p>Currently, it can do the following:</p>
<ul>
<li>Install the WireGuard server.</li>
<li>Create the first client.</li>
<li>Add / remove clients.</li>
<li>Automatically allocate client IPs.</li>
<li>Generate client configuration files.</li>
<li>Display a QR code in the terminal for direct scanning with a phone.</li>
<li>Support global mode and simple split tunneling mode.</li>
<li>Check service status and peer information.</li>
<li>Rebuild configurations and reload.</li>
<li>Uninstall configurations managed by the script.</li>
</ul>
<p>It&#39;s suitable for a Debian / Ubuntu cloud server.</p>
<p>It is not suitable for scenarios that already have a complex WireGuard production environment, lots of policy routing, and highly specific split tunneling rules. This script isn&#39;t meant to take over complex networks; it&#39;s mainly to streamline the most common workflows for everyday users.</p>
<h2 id="why-write-a-menu-mode">Why Write a Menu Mode</h2><p>Usually, scripts like this start out using command-line arguments:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh add-client iphone
<span class="hljs-built_in">sudo</span> ./wg-manager.sh qr iphone
<span class="hljs-built_in">sudo</span> ./wg-manager.sh status</code></pre><p>It works, but you have to memorize the commands.</p>
<p>When doing manual operations on a day-to-day basis, I&#39;d rather just see a menu:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh</code></pre><p>And then choose:</p>
<pre><code class="language-text">1) Install WireGuard
2) Add a client
3) Delete a client
4) List clients
5) View client configuration
6) Show client QR code
7) Check service status
8) Reload configuration
9) Uninstall</code></pre><p>This way, you don&#39;t have to check the README every time.</p>
<p>There is also a detail: when starting in menu mode, it will first check if you are root.</p>
<p>If you didn&#39;t use <code>sudo</code>, it exits immediately. You don&#39;t want someone filling out a bunch of prompts only to be told at the end that they lack permissions. That kind of experience is a bit absurd.</p>
<h2 id="getting-the-script">Getting the Script</h2><p>If the repository is already public, you can clone it directly:</p>
<pre><code class="language-bash">git <span class="hljs-built_in">clone</span> https://github.com/Kaurisss/wg-manager.git
<span class="hljs-built_in">cd</span> wg-manager
<span class="hljs-built_in">chmod</span> +x wg-manager.sh</code></pre><p>Or you can just download the single file:</p>
<pre><code class="language-bash">curl -fsSL -o wg-manager.sh https://raw.githubusercontent.com/Kaurisss/wg-manager/main/wg-manager.sh
<span class="hljs-built_in">chmod</span> +x wg-manager.sh</code></pre><p>It is recommended to run the script on a server. Don&#39;t force it to run locally on Windows; this thing is intended for Linux servers anyway.</p>
<h2 id="using-the-menu-mode">Using the Menu Mode</h2><p>The simplest way:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh</code></pre><p>For the first time, choose to install WireGuard.</p>
<p>The script will ask you to fill in these details:</p>
<ul>
<li>WireGuard interface name, default <code>wg0</code></li>
<li>UDP port, default <code>51820</code></li>
<li>IPv4 subnet, default <code>10.66.66.0/24</code></li>
<li>Whether to enable IPv6</li>
<li>Client DNS</li>
<li>Name of the first client</li>
<li>Client mode: Global / Split tunneling</li>
</ul>
<p>For most of them, you can just press Enter to use the default values.</p>
<p>After installation, it will generate the server configuration and conveniently generate the first client configuration as well.</p>
<p>If <code>qrencode</code> is installed, it can also display a QR code right in the terminal. You can just scan and import it with the WireGuard mobile client, which is quite handy.</p>
<h2 id="command-line-mode">Command-Line Mode</h2><p>The menu is great for manual clicking.</p>
<p>But for writing documentation, automation, and copying commands, subcommands still need to be preserved.</p>
<p>Install and create the first client:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh install --client-name phone</code></pre><p>Enable IPv6:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh install --enable-ipv6 --client-name phone</code></pre><p>Custom port and subnet:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh install \
  --port 51820 \
  --ipv4-net 10.66.66.0/24 \
  --enable-ipv6 \
  --ipv6-net fd66:66:66::/64 \
  --client-name laptop</code></pre><p>Add a client:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh add-client iphone</code></pre><p>Show QR code:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh qr iphone</code></pre><p>Check status:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh status</code></pre><p>List clients:</p>
<pre><code class="language-bash">./wg-manager.sh list-clients</code></pre><p>Reload configuration:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh reload</code></pre><p>Uninstall:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh uninstall --<span class="hljs-built_in">yes</span></code></pre><h2 id="global-mode-and-split-tunneling-mode">Global Mode and Split Tunneling Mode</h2><p>The script offers two client modes.</p>
<h3 id="global-mode">Global Mode</h3><p>Global mode means all client traffic goes through WireGuard:</p>
<pre><code class="language-text">AllowedIPs = 0.0.0.0/0,::/0</code></pre><p>Suitable for:</p>
<ul>
<li>Connecting back to the server when you&#39;re out with your phone.</li>
<li>Wanting all traffic to pass through this VPS.</li>
<li>Not wanting to bother with split tunneling rules.</li>
</ul>
<h3 id="split-tunneling-mode">Split Tunneling Mode</h3><p>Split tunneling mode means only designated subnets go through WireGuard.</p>
<p>For example, routing only intranet addresses:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh add-client office \
  --mode <span class="hljs-built_in">split</span> \
  --allowed-ips 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16</code></pre><p>Note that this only uses <code>AllowedIPs</code> for simple split tunneling.</p>
<p>It is not intelligent routing; it won&#39;t automatically recognize games, websites, or domains. If you want highly specific rules, you&#39;ll have to use a more complex solution.</p>
<h2 id="where-are-the-generated-files-stored">Where Are the Generated Files Stored?</h2><p>By default, they are all in <code>/etc/wireguard/</code>:</p>
<pre><code class="language-text">/etc/wireguard/
├── wg0.conf
├── clients/
│   ├── phone.conf
│   └── iphone.conf
├── peers/
│   └── iphone/
│       ├── private.key
│       ├── public.key
│       ├── psk.key
│       └── meta
├── backup/
└── .wg-manager/
    ├── server.env
    ├── server_private.key
    └── server_public.key</code></pre><p>This contains private keys, PSKs, and client configurations.</p>
<p>Do not push these to GitHub.</p>
<p>In a public repository, you should only put the script, README, and example configurations. Uploading real configuration files is basically digging a hole for yourself.</p>
<h2 id="security-groups-and-firewalls">Security Groups and Firewalls</h2><p>The script attempts to handle UFW and firewalld, automatically allowing WireGuard&#39;s UDP port.</p>
<p>But cloud servers also have security groups.</p>
<p>This script cannot manage the cloud provider&#39;s console.</p>
<p>If you can&#39;t connect, check these first:</p>
<ul>
<li>Has the cloud server&#39;s security group allowed UDP <code>51820</code>?</li>
<li>Is the local firewall blocking it?</li>
<li>Did you mistype the endpoint in the client?</li>
<li>Has the server&#39;s public IP changed?</li>
<li>Is there any handshake in <code>sudo ./wg-manager.sh status</code>?</li>
</ul>
<p>Don&#39;t immediately blame the script if you can&#39;t connect.</p>
<p>With networking, it&#39;s often not that the configuration wasn&#39;t generated; it&#39;s that the traffic simply didn&#39;t come in.</p>
<h2 id="troubleshooting-order">Troubleshooting Order</h2><p>I generally follow this order:</p>
<h3 id="1.-is-the-service-up">1. Is the Service Up?</h3><pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh status</code></pre><p>Or:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> systemctl status wg-quick@wg0</code></pre><h3 id="2.-is-the-client-configuration-generated">2. Is the Client Configuration Generated?</h3><pre><code class="language-bash"><span class="hljs-built_in">ls</span> -l /etc/wireguard/clients/</code></pre><h3 id="3.-can-the-qr-code-be-scanned">3. Can the QR Code Be Scanned?</h3><pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ./wg-manager.sh qr iphone</code></pre><h3 id="4.-have-the-peers-handshaked">4. Have the Peers Handshaked?</h3><pre><code class="language-bash"><span class="hljs-built_in">sudo</span> wg</code></pre><p>Having a <code>latest handshake</code> is the only way to prove the client has actually connected.</p>
<p>If there&#39;s no handshake, continue checking the port, security group, endpoint, and public/private keys.</p>
<h2 id="known-limitations">Known Limitations</h2><p>This script is not a silver bullet.</p>
<p>Current limitations include roughly:</p>
<ul>
<li>Only supports Debian / Ubuntu.</li>
<li>IPv4 subnets are only handled as <code>/24</code>.</li>
<li>IPv6 subnets are only handled as <code>/64</code>.</li>
<li>Uses <code>iptables</code> / <code>ip6tables</code> for NAT by default.</li>
<li>Does not take over existing complex WireGuard environments.</li>
<li>Split tunneling is just simple <code>AllowedIPs</code>; no intelligent rules.</li>
</ul>
<p>These limitations aren&#39;t bugs; they are a deliberate control of scope.</p>
<p>For scripts like this, the worst thing is trying to cover everything from the start, and ending up with a mess that no one dares to run.</p>
<h2 id="final-words">Final Words</h2><p><code>wg-manager</code> isn&#39;t meant to replace the official WireGuard tools.</p>
<p>It merely wraps the few things I frequently do into a script: installing, adding clients, deleting clients, scanning QR codes, checking status, and reloading configurations.</p>
<p>Every time I can avoid manually editing <code>wg0.conf</code> is one less chance to write the configuration incorrectly.</p>
<p>Getting it to run stably first, and then slowly adding features later. That direction feels a lot more human.</p>
]]></content>
  </entry>
  <entry>
    <title>Hello OpenCode</title>
    <link href="https://blog.kauriss.site/en/2026/01/13/hello-opencode/" />
    <id>https://blog.kauriss.site/en/2026/01/13/hello-opencode/</id>
    <updated>2026-01-13T18:46:00.000Z</updated>
    <summary>This blog is currently using Hexo + the OpenCode theme. Initially, I just wanted a lightweight theme instead of those flashy template sites. Later, I came across the OpenCode termi</summary>
    <content type="html"><![CDATA[<p>This blog is currently using Hexo + the OpenCode theme.</p>
<p>Initially, I just wanted a lightweight theme instead of those flashy template sites. Later, I came across the OpenCode terminal style, which looked pretty good—black and white, monospace fonts, and a relatively clean structure, so I decided to use it for now.</p>
<p><img src="/images/opencode_theme.png" alt="OpenCode Theme Preview"></p>
<h2 id="why-i-use-this-theme">Why I use this theme</h2><p>Mainly for a few reasons:</p>
<ul>
<li>The pages are clean and don&#39;t distract from the article content.</li>
<li>Monospace fonts make reading code comfortable.</li>
<li>The black and white style is timeless and doesn&#39;t easily cause aesthetic fatigue.</li>
<li>The structure is simple, making future modifications relatively easy.</li>
</ul>
<p>Some themes look dazzling at first glance, but when you actually write articles, they become a mess: too many cards, chaotic colors, and a bunch of animations. They look less like blogs and more like landing pages for selling courses.</p>
<p>This one, at least, isn&#39;t that abstract.</p>
<h2 id="current-state">Current state</h2><p>Currently, the blog mainly hosts these contents:</p>
<ul>
<li>Logs of tinkering with networks and servers.</li>
<li>Linux, scripts, and command cheat sheets.</li>
<li>Notes on tool deployment and troubleshooting pitfalls.</li>
<li>Later, I might post about AI coding, websites, and automation.</li>
</ul>
<p>The writing doesn&#39;t have to be too formal; as long as it&#39;s understandable and reproducible, that&#39;s fine.</p>
<h2 id="code-block-effect">Code block effect</h2><p>Command blocks look roughly like this:</p>
<pre><code class="language-bash">npm install
npm run build</code></pre><p>Regular code blocks:</p>
<pre><code class="language-javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">log</span>(<span class="hljs-params">message</span>) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`[stdout] <span class="hljs-subst">${message}</span>`</span>)
}

<span class="hljs-title function_">log</span>(<span class="hljs-string">&#x27;hello opencode&#x27;</span>)</code></pre><h2 id="potential-future-tweaks">Potential future tweaks</h2><p>The theme is usable, but it&#39;s not like I won&#39;t touch it at all.</p>
<p>I&#39;ll probably tweak these things:</p>
<ol>
<li>Make the homepage article list a bit more compact.</li>
<li>Adjust Chinese typography details.</li>
<li>Ensure category and tag pages aren&#39;t too empty.</li>
<li>Rewrite the code block copy button if it doesn&#39;t work well.</li>
<li>Continue fixing things that look off on mobile.</li>
</ol>
<p>Get it running first, then slowly tweak it.</p>
<h2 id="conclusion">Conclusion</h2><p>Right now, this site just serves as my personal technical notes.</p>
<p>Writing articles isn&#39;t about pretending to be professional; it&#39;s mostly to prevent forgetting how to configure things next time. If I can document the pitfalls, the effort wasn&#39;t wasted.</p>
]]></content>
  </entry>
  <entry>
    <title>Linux Commands Cheat Sheet</title>
    <link href="https://blog.kauriss.site/en/2025/11/28/%E5%B8%B8%E7%94%A8%20Linux%20%E5%91%BD%E4%BB%A4%E9%80%9F%E6%9F%A5%E8%A1%A8/" />
    <id>https://blog.kauriss.site/en/2025/11/28/%E5%B8%B8%E7%94%A8%20Linux%20%E5%91%BD%E4%BB%A4%E9%80%9F%E6%9F%A5%E8%A1%A8/</id>
    <updated>2025-11-28T00:00:00.000Z</updated>
    <summary>A Linux cheat sheet for quick reference, mainly containing common commands, parameters, and a few copy pasteable examples.</summary>
    <content type="html"><![CDATA[<p>This is just for my own reference to look up commands.</p>
<p>There are too many Linux commands to memorize them all. It&#39;s enough to remember the common ones and be able to look up the rest quickly when needed.</p>
<p><img src="/images/shell.png" alt=""></p>
<h2 id="table-of-contents">Table of Contents</h2><ol>
<li><a href="#quick-reference-table">Quick Reference Table</a></li>
<li><a href="#files-and-directories">Files and Directories</a></li>
<li><a href="#viewing-files">Viewing Files</a></li>
<li><a href="#text-processing">Text Processing</a></li>
<li><a href="#finding-files">Finding Files</a></li>
<li><a href="#permissions-and-users">Permissions and Users</a></li>
<li><a href="#processes-and-services">Processes and Services</a></li>
<li><a href="#networking">Networking</a></li>
<li><a href="#package-management">Package Management</a></li>
<li><a href="#disks-and-mounting">Disks and Mounting</a></li>
<li><a href="#compression-and-archiving">Compression and Archiving</a></li>
<li><a href="#logs-and-troubleshooting">Logs and Troubleshooting</a></li>
<li><a href="#shell-miscellany">Shell Miscellany</a></li>
<li><a href="#common-one-liners">Common One-Liners</a></li>
</ol>
<h2 id="quick-reference-table">Quick Reference Table</h2><table>
<thead>
<tr>
<th align="left">Task</th>
<th align="left">Common Commands</th>
</tr>
</thead>
<tbody><tr>
<td align="left">View directories</td>
<td align="left"><code>ls</code>, <code>tree</code></td>
</tr>
<tr>
<td align="left">Change directories</td>
<td align="left"><code>cd</code>, <code>pwd</code></td>
</tr>
<tr>
<td align="left">Copy, move, delete</td>
<td align="left"><code>cp</code>, <code>mv</code>, <code>rm</code></td>
</tr>
<tr>
<td align="left">View files</td>
<td align="left"><code>cat</code>, <code>less</code>, <code>head</code>, <code>tail</code></td>
</tr>
<tr>
<td align="left">Search text</td>
<td align="left"><code>grep</code>, <code>rg</code></td>
</tr>
<tr>
<td align="left">Process text</td>
<td align="left"><code>awk</code>, <code>sed</code>, <code>cut</code>, <code>sort</code>, <code>uniq</code></td>
</tr>
<tr>
<td align="left">Find files</td>
<td align="left"><code>find</code>, <code>locate</code>, <code>which</code></td>
</tr>
<tr>
<td align="left">Change permissions</td>
<td align="left"><code>chmod</code>, <code>chown</code></td>
</tr>
<tr>
<td align="left">View processes</td>
<td align="left"><code>ps</code>, <code>top</code>, <code>htop</code>, <code>kill</code></td>
</tr>
<tr>
<td align="left">Networking</td>
<td align="left"><code>ip</code>, <code>ss</code>, <code>curl</code>, <code>ping</code>, <code>ssh</code></td>
</tr>
<tr>
<td align="left">Archive and compress</td>
<td align="left"><code>tar</code>, <code>zip</code>, <code>gzip</code></td>
</tr>
</tbody></table>
<h2 id="files-and-directories">Files and Directories</h2><h3 id="ls"><code>ls</code></h3><p>View directories:</p>
<pre><code class="language-bash"><span class="hljs-built_in">ls</span>
<span class="hljs-built_in">ls</span> -l
<span class="hljs-built_in">ls</span> -la
<span class="hljs-built_in">ls</span> -lh</code></pre><p>Common parameters:</p>
<ul>
<li><code>-l</code>: Detailed list</li>
<li><code>-a</code>: Show hidden files</li>
<li><code>-h</code>: Human-readable file sizes, e.g., <code>1.2G</code></li>
</ul>
<h3 id="cd-and-pwd"><code>cd</code> and <code>pwd</code></h3><pre><code class="language-bash"><span class="hljs-built_in">pwd</span>
<span class="hljs-built_in">cd</span> /var/www
<span class="hljs-built_in">cd</span> ~
<span class="hljs-built_in">cd</span> -</code></pre><p><code>cd -</code> takes you back to the previous directory, which is quite useful.</p>
<h3 id="cp"><code>cp</code></h3><pre><code class="language-bash"><span class="hljs-built_in">cp</span> a.txt b.txt
<span class="hljs-built_in">cp</span> -r src/ backup/
<span class="hljs-built_in">cp</span> -a src/ backup/</code></pre><p><code>-a</code> attempts to preserve permissions, timestamps, and other information, making it more reliable than <code>-r</code> when backing up directories.</p>
<h3 id="mv"><code>mv</code></h3><pre><code class="language-bash"><span class="hljs-built_in">mv</span> old.txt new.txt
<span class="hljs-built_in">mv</span> file.txt /tmp/</code></pre><p>Can be used for both moving and renaming.</p>
<h3 id="rm"><code>rm</code></h3><pre><code class="language-bash"><span class="hljs-built_in">rm</span> file.txt
<span class="hljs-built_in">rm</span> -r folder/
<span class="hljs-built_in">rm</span> -rf folder/</code></pre><p>Be careful not to slip up with <code>rm -rf</code>. Especially when using variables, <code>echo</code> the path first, otherwise you will regret it if you delete the wrong thing.</p>
<h3 id="mkdir"><code>mkdir</code></h3><pre><code class="language-bash"><span class="hljs-built_in">mkdir</span> logs
<span class="hljs-built_in">mkdir</span> -p app/data/cache</code></pre><p><code>-p</code> automatically creates intermediate directories.</p>
<h2 id="viewing-files">Viewing Files</h2><h3 id="cat"><code>cat</code></h3><pre><code class="language-bash"><span class="hljs-built_in">cat</span> file.txt</code></pre><p>View small files directly. Don&#39;t use it for large files as it will flood your screen.</p>
<h3 id="less"><code>less</code></h3><pre><code class="language-bash">less app.log</code></pre><p>Common operations:</p>
<ul>
<li><code>/keyword</code>: Search</li>
<li><code>n</code>: Next result</li>
<li><code>q</code>: Quit</li>
</ul>
<h3 id="head-and-tail"><code>head</code> and <code>tail</code></h3><pre><code class="language-bash"><span class="hljs-built_in">head</span> -n 20 app.log
<span class="hljs-built_in">tail</span> -n 50 app.log
<span class="hljs-built_in">tail</span> -f app.log</code></pre><p>Generally use <code>tail -f</code> for viewing logs.</p>
<h3 id="nl"><code>nl</code></h3><pre><code class="language-bash"><span class="hljs-built_in">nl</span> file.txt</code></pre><p>Displays files with line numbers. Useful when troubleshooting configurations.</p>
<h2 id="text-processing">Text Processing</h2><h3 id="grep"><code>grep</code></h3><pre><code class="language-bash">grep <span class="hljs-string">&quot;ERROR&quot;</span> app.log
grep -n <span class="hljs-string">&quot;ERROR&quot;</span> app.log
grep -R <span class="hljs-string">&quot;listen&quot;</span> /etc/nginx
grep -i <span class="hljs-string">&quot;error&quot;</span> app.log</code></pre><p>Common parameters:</p>
<ul>
<li><code>-n</code>: Show line numbers</li>
<li><code>-R</code>: Search directories recursively</li>
<li><code>-i</code>: Ignore case</li>
</ul>
<p>If <code>rg</code> (ripgrep) is available on the system, I usually prefer it as it&#39;s much faster:</p>
<pre><code class="language-bash">rg <span class="hljs-string">&quot;ERROR&quot;</span>
rg <span class="hljs-string">&quot;listen&quot;</span> /etc/nginx</code></pre><h3 id="sed"><code>sed</code></h3><pre><code class="language-bash">sed -n <span class="hljs-string">&#x27;1,20p&#x27;</span> file.txt
sed <span class="hljs-string">&#x27;s/old/new/g&#x27;</span> file.txt</code></pre><p>Often used for replacing text or just viewing specific lines.</p>
<h3 id="awk"><code>awk</code></h3><pre><code class="language-bash">awk <span class="hljs-string">&#x27;{print $1}&#x27;</span> access.log
awk <span class="hljs-string">&#x27;{print $1, $9}&#x27;</span> access.log</code></pre><p>Processes text by columns. Frequently used for log analysis.</p>
<h3 id="cut"><code>cut</code></h3><pre><code class="language-bash"><span class="hljs-built_in">cut</span> -d<span class="hljs-string">&#x27;:&#x27;</span> -f1 /etc/passwd
<span class="hljs-built_in">cut</span> -d<span class="hljs-string">&#x27;,&#x27;</span> -f1,3 data.csv</code></pre><p>Use this for simple column splitting; no need to write complex scripts right away.</p>
<h3 id="sort-and-uniq"><code>sort</code> and <code>uniq</code></h3><pre><code class="language-bash"><span class="hljs-built_in">sort</span> file.txt
<span class="hljs-built_in">sort</span> -n numbers.txt
<span class="hljs-built_in">sort</span> file.txt | <span class="hljs-built_in">uniq</span>
<span class="hljs-built_in">sort</span> file.txt | <span class="hljs-built_in">uniq</span> -c</code></pre><p>Very handy for counting duplicates.</p>
<h3 id="wc"><code>wc</code></h3><pre><code class="language-bash"><span class="hljs-built_in">wc</span> -l file.txt
<span class="hljs-built_in">wc</span> -c file.txt</code></pre><p>Counts lines and bytes.</p>
<h2 id="finding-files">Finding Files</h2><h3 id="find"><code>find</code></h3><pre><code class="language-bash">find . -name <span class="hljs-string">&quot;*.log&quot;</span>
find /var/log -<span class="hljs-built_in">type</span> f -mtime -7
find . -<span class="hljs-built_in">type</span> f -size +100M</code></pre><p>A few common conditions:</p>
<ul>
<li><code>-name</code>: By name</li>
<li><code>-type f</code>: Find files only</li>
<li><code>-type d</code>: Find directories only</li>
<li><code>-mtime -7</code>: Modified within 7 days</li>
<li><code>-size +100M</code>: Larger than 100M</li>
</ul>
<h3 id="which"><code>which</code></h3><pre><code class="language-bash"><span class="hljs-built_in">which</span> nginx
<span class="hljs-built_in">which</span> python</code></pre><p>Shows exactly where a command is coming from.</p>
<h3 id="locate"><code>locate</code></h3><pre><code class="language-bash">locate nginx.conf</code></pre><p>Fast, but relies on an index. Newly created files might not be found.</p>
<h2 id="permissions-and-users">Permissions and Users</h2><h3 id="chmod"><code>chmod</code></h3><pre><code class="language-bash"><span class="hljs-built_in">chmod</span> 644 file.txt
<span class="hljs-built_in">chmod</span> 755 script.sh
<span class="hljs-built_in">chmod</span> +x script.sh</code></pre><p>Common permissions:</p>
<ul>
<li><code>644</code>: Regular files</li>
<li><code>755</code>: Executable scripts or directories</li>
<li><code>600</code>: Sensitive files like private keys</li>
</ul>
<h3 id="chown"><code>chown</code></h3><pre><code class="language-bash"><span class="hljs-built_in">sudo</span> <span class="hljs-built_in">chown</span> user:group file.txt
<span class="hljs-built_in">sudo</span> <span class="hljs-built_in">chown</span> -R www-data:www-data /var/www/app</code></pre><p>You often need to check this when website directory permissions are incorrect.</p>
<h3 id="user-related">User-related</h3><pre><code class="language-bash"><span class="hljs-built_in">whoami</span>
<span class="hljs-built_in">id</span>
passwd
<span class="hljs-built_in">sudo</span> useradd name
<span class="hljs-built_in">sudo</span> usermod -aG <span class="hljs-built_in">sudo</span> name</code></pre><h2 id="processes-and-services">Processes and Services</h2><h3 id="viewing-processes">Viewing Processes</h3><pre><code class="language-bash">ps aux
ps aux | grep nginx
top
htop</code></pre><p>Use <code>htop</code> if you have it; it looks nicer.</p>
<h3 id="killing-processes">Killing Processes</h3><pre><code class="language-bash"><span class="hljs-built_in">kill</span> 1234
<span class="hljs-built_in">kill</span> -9 1234
pkill -f nginx</code></pre><p><code>kill -9</code> is a forceful kill, not a universal fix button. Try a normal <code>kill</code> first, and use it only if that fails.</p>
<h3 id="systemd-services">systemd Services</h3><pre><code class="language-bash"><span class="hljs-built_in">sudo</span> systemctl status nginx
<span class="hljs-built_in">sudo</span> systemctl start nginx
<span class="hljs-built_in">sudo</span> systemctl restart nginx
<span class="hljs-built_in">sudo</span> systemctl <span class="hljs-built_in">enable</span> nginx</code></pre><p>View service logs:</p>
<pre><code class="language-bash">journalctl -u nginx -f
journalctl -u nginx --since <span class="hljs-string">&quot;1 hour ago&quot;</span></code></pre><h2 id="networking">Networking</h2><h3 id="ip-and-ports">IP and Ports</h3><pre><code class="language-bash">ip addr
ip route
ss -tuln
ss -tulnp</code></pre><p><code>ss -tulnp</code> shows which process is occupying which port.</p>
<h3 id="connectivity">Connectivity</h3><pre><code class="language-bash">ping 1.1.1.1
curl -I https://example.com
curl http://127.0.0.1:3000</code></pre><p>Don&#39;t just <code>ping</code> to test HTTP services. A successful ping doesn&#39;t mean the website is alive.</p>
<h3 id="ssh">SSH</h3><pre><code class="language-bash">ssh root@1.2.3.4
ssh -p 2222 user@1.2.3.4
scp file.txt user@1.2.3.4:/tmp/</code></pre><h3 id="rsync">rsync</h3><pre><code class="language-bash">rsync -avz ./dist/ user@server:/var/www/app/
rsync -avz --delete ./dist/ user@server:/var/www/app/</code></pre><p><code>--delete</code> removes extra files on the destination side, so think carefully before using it.</p>
<h2 id="package-management">Package Management</h2><p>Debian / Ubuntu:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> apt update
<span class="hljs-built_in">sudo</span> apt install nginx
<span class="hljs-built_in">sudo</span> apt upgrade</code></pre><p>CentOS / RHEL:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> yum install nginx
<span class="hljs-built_in">sudo</span> dnf install nginx</code></pre><p>Arch:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> pacman -S nginx</code></pre><p>openSUSE:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> zypper install nginx</code></pre><p>Commands vary across distributions. Make sure not to copy the wrong one for your system.</p>
<h2 id="disks-and-mounting">Disks and Mounting</h2><h3 id="viewing-disks">Viewing Disks</h3><pre><code class="language-bash"><span class="hljs-built_in">df</span> -h
<span class="hljs-built_in">du</span> -sh *
<span class="hljs-built_in">du</span> -sh /var/log</code></pre><p>Finding large directories:</p>
<pre><code class="language-bash"><span class="hljs-built_in">du</span> -h --max-depth=1 / | <span class="hljs-built_in">sort</span> -h</code></pre><h3 id="block-devices">Block Devices</h3><pre><code class="language-bash">lsblk
blkid
<span class="hljs-built_in">sudo</span> fdisk -l</code></pre><h3 id="mounting">Mounting</h3><pre><code class="language-bash"><span class="hljs-built_in">sudo</span> mount /dev/sdb1 /mnt
<span class="hljs-built_in">sudo</span> umount /mnt</code></pre><h2 id="compression-and-archiving">Compression and Archiving</h2><h3 id="tar">tar</h3><pre><code class="language-bash">tar -czvf archive.tar.gz folder/
tar -xzvf archive.tar.gz
tar -tzf archive.tar.gz</code></pre><h3 id="zip">zip</h3><pre><code class="language-bash">zip -r archive.zip folder/
unzip archive.zip</code></pre><h3 id="gzip">gzip</h3><pre><code class="language-bash">gzip file.txt
gunzip file.txt.gz</code></pre><h2 id="logs-and-troubleshooting">Logs and Troubleshooting</h2><pre><code class="language-bash">dmesg
journalctl -xe
journalctl -u service-name -f
<span class="hljs-built_in">tail</span> -f /var/log/syslog
<span class="hljs-built_in">tail</span> -f /var/log/nginx/error.log</code></pre><p>Port is occupied:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> lsof -i :80
<span class="hljs-built_in">sudo</span> ss -tulnp | grep :80</code></pre><p>If configuration changes don&#39;t take effect, first check if the service was restarted, then check the logs. Many problems aren&#39;t due to missing configurations, but because the service simply didn&#39;t reload them.</p>
<h2 id="shell-miscellany">Shell Miscellany</h2><h3 id="environment-variables">Environment Variables</h3><pre><code class="language-bash"><span class="hljs-built_in">env</span>
<span class="hljs-built_in">printenv</span>
<span class="hljs-built_in">export</span> NODE_ENV=production
<span class="hljs-built_in">echo</span> <span class="hljs-variable">$PATH</span></code></pre><h3 id="alias">alias</h3><pre><code class="language-bash"><span class="hljs-built_in">alias</span> ll=<span class="hljs-string">&#x27;ls -lah&#x27;</span>
<span class="hljs-built_in">alias</span> gs=<span class="hljs-string">&#x27;git status&#x27;</span></code></pre><p>Add them to <code>~/.bashrc</code> or <code>~/.zshrc</code> to make them permanent.</p>
<h3 id="executing-scripts">Executing Scripts</h3><pre><code class="language-bash"><span class="hljs-built_in">chmod</span> +x script.sh
./script.sh</code></pre><h3 id="cron-jobs">Cron Jobs</h3><pre><code class="language-bash">crontab -e
crontab -l</code></pre><h2 id="common-one-liners">Common One-Liners</h2><p>Find large files:</p>
<pre><code class="language-bash">find / -<span class="hljs-built_in">type</span> f -size +500M 2&gt;/dev/null</code></pre><p>Search for errors in logs:</p>
<pre><code class="language-bash">grep -R --line-number <span class="hljs-string">&quot;ERROR&quot;</span> /var/log 2&gt;/dev/null</code></pre><p>View the size of each folder in the current directory:</p>
<pre><code class="language-bash"><span class="hljs-built_in">du</span> -h --max-depth=1 . | <span class="hljs-built_in">sort</span> -h</code></pre><p>Sync website build artifacts:</p>
<pre><code class="language-bash">rsync -avz --delete ./dist/ user@server:/var/www/site/</code></pre><p>View recent logs for a service:</p>
<pre><code class="language-bash">journalctl -u nginx --since <span class="hljs-string">&quot;2 hours ago&quot;</span> -f</code></pre><p>Find the process occupying a port:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> ss -tulnp | grep :3000</code></pre><p>Batch delete <code>.tmp</code> files:</p>
<pre><code class="language-bash">find . -<span class="hljs-built_in">type</span> f -name <span class="hljs-string">&quot;*.tmp&quot;</span> -delete</code></pre><p>It&#39;s best to remove <code>-delete</code> and preview the results before executing this command:</p>
<pre><code class="language-bash">find . -<span class="hljs-built_in">type</span> f -name <span class="hljs-string">&quot;*.tmp&quot;</span></code></pre><p>Don&#39;t delete right away; deleting the wrong files is quite troublesome.</p>
]]></content>
  </entry>
  <entry>
    <title>Cloudflare Tunnel Deployment Guide</title>
    <link href="https://blog.kauriss.site/en/2025/11/21/Cloudflare-Tunnel-%E9%83%A8%E7%BD%B2%E6%95%99%E7%A8%8B/" />
    <id>https://blog.kauriss.site/en/2025/11/21/Cloudflare-Tunnel-%E9%83%A8%E7%BD%B2%E6%95%99%E7%A8%8B/</id>
    <updated>2025-11-21T00:00:00.000Z</updated>
    <summary>Use Cloudflare Tunnel to expose intranet services to the public internet without a public IP or opening ports on your router.</summary>
    <content type="html"><![CDATA[<p>Sometimes you want to expose a home computer, Raspberry Pi, or an intranet server to the public internet, but you don&#39;t have a public IP and tweaking the router is a hassle.</p>
<p>This is where Cloudflare Tunnel comes in handy. The logic is simple: your local machine actively connects to Cloudflare, and people outside access this tunnel via your domain name. You don&#39;t need to expose ports, and you don&#39;t have to care if your ISP provides a public IP.</p>
<p><img src="/images/CF_logo_horizontal_blktype.svg" alt=""></p>
<h2 id="use-cases">Use Cases</h2><ul>
<li>Running a website at home and wanting to temporarily expose it to the external network.</li>
<li>Binding a domain to a NAS, panel, or test service.</li>
<li>Avoiding opening a bunch of ports on the router.</li>
<li>Wanting HTTPS but lacking a public IP.</li>
</ul>
<p>Do not treat it as a universal reverse proxy. If you are running high-concurrency, high-traffic services, just use a proper server. Tunnel is more suited for personal services, test environments, and low-frequency access.</p>
<h2 id="prerequisites">Prerequisites</h2><p>Confirm these things first:</p>
<table>
<thead>
<tr>
<th align="left">Item</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Cloudflare Account</td>
<td align="left">The domain must already be hosted on Cloudflare</td>
</tr>
<tr>
<td align="left">A local machine</td>
<td align="left">Linux is the easiest, Windows works too, but this guide focuses on Linux</td>
</tr>
<tr>
<td align="left">A local service</td>
<td align="left">For example, <code>http://127.0.0.1:80</code>, <code>http://127.0.0.1:8080</code></td>
</tr>
<tr>
<td align="left">A domain name</td>
<td align="left">For example, <code>app.example.com</code></td>
</tr>
</tbody></table>
<p>First, test if your local service is alive:</p>
<pre><code class="language-bash">curl -I http://127.0.0.1:80</code></pre><p>Seeing a normal response like <code>200</code>, <code>301</code>, or <code>302</code> is fine. If you can&#39;t even access it locally, don&#39;t mess with Tunnel yet, otherwise troubleshooting will be very abstract.</p>
<h2 id="final-result">Final Result</h2><p>After finishing, the external network will access your domain:</p>
<pre><code class="language-text">https://app.example.com</code></pre><p>The request will enter through Cloudflare and then be forwarded to the intranet service via the active Tunnel established by your local machine. You don&#39;t need port mapping on the router, nor do you need a public IP at home.</p>
<h2 id="install-cloudflared">Install cloudflared</h2><p><code>cloudflared</code> is the client for Cloudflare Tunnel.</p>
<p>On macOS, you can just use Homebrew:</p>
<pre><code class="language-bash">brew install cloudflared</code></pre><p>For Linux, just download the binary file:</p>
<pre><code class="language-bash">curl -L <span class="hljs-string">&#x27;https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64&#x27;</span> -o ./cloudflared
<span class="hljs-built_in">chmod</span> +x ./cloudflared
<span class="hljs-built_in">sudo</span> <span class="hljs-built_in">mv</span> ./cloudflared /usr/local/bin/cloudflared</code></pre><p>Check if it is installed properly:</p>
<pre><code class="language-bash">cloudflared --version</code></pre><p>Having a version number is enough.</p>
<h2 id="log-in-to-cloudflare">Log in to Cloudflare</h2><p>Let the local <code>cloudflared</code> obtain Cloudflare account authorization:</p>
<pre><code class="language-bash">cloudflared tunnel login</code></pre><p>The command will provide a link. Copy and open it in your browser, log in to Cloudflare, select the domain you want to use, and then authorize it.</p>
<p>After successful authorization, the local machine will generate a certificate file. Don&#39;t randomly delete this file; it will be needed later to create the tunnel.</p>
<h2 id="create-a-tunnel">Create a Tunnel</h2><p>Give the tunnel a name, for example, <code>home-web</code>:</p>
<pre><code class="language-bash">cloudflared tunnel create home-web</code></pre><p>The output will contain two key pieces of information:</p>
<pre><code class="language-text">Created tunnel home-web with id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Tunnel credentials written to /root/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json</code></pre><p>Remember these two:</p>
<ul>
<li>Tunnel ID</li>
<li>credentials file path</li>
</ul>
<p>These will need to be filled in the configuration file later.</p>
<h2 id="write-the-configuration-file">Write the Configuration File</h2><p>Create the configuration directory:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> <span class="hljs-built_in">mkdir</span> -p /etc/cloudflared
<span class="hljs-built_in">sudo</span> nano /etc/cloudflared/config.yml</code></pre><p>Example configuration:</p>
<pre><code class="language-yaml"><span class="hljs-attr">tunnel:</span> <span class="hljs-string">xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</span>
<span class="hljs-attr">credentials-file:</span> <span class="hljs-string">/root/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json</span>

<span class="hljs-attr">ingress:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">hostname:</span> <span class="hljs-string">app.example.com</span>
    <span class="hljs-attr">service:</span> <span class="hljs-string">http://127.0.0.1:80</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">hostname:</span> <span class="hljs-string">admin.example.com</span>
    <span class="hljs-attr">service:</span> <span class="hljs-string">http://127.0.0.1:8080</span>

  <span class="hljs-bullet">-</span> <span class="hljs-attr">service:</span> <span class="hljs-string">http_status:404</span></code></pre><p>Don&#39;t just copy the domain name here. Replace it with your own.</p>
<p><code>ingress</code> is matched in order, so it&#39;s recommended to keep the last <code>404</code>. Otherwise, it gets messy handling unmatched requests.</p>
<p>After writing the configuration, check it first:</p>
<pre><code class="language-bash">cloudflared tunnel ingress validate</code></pre><p>If it passes, continue.</p>
<h2 id="bind-the-domain-name">Bind the Domain Name</h2><p>Point the domain to this Tunnel:</p>
<pre><code class="language-bash">cloudflared tunnel route dns home-web app.example.com
cloudflared tunnel route dns home-web admin.example.com</code></pre><p>If a DNS record conflict is prompted, go to the Cloudflare dashboard, delete the old <code>A</code>, <code>AAAA</code>, and <code>CNAME</code> records, and execute it again.</p>
<h2 id="run-manually-first">Run Manually First</h2><p>Don&#39;t rush to install the service, run it first to see:</p>
<pre><code class="language-bash">cloudflared tunnel run home-web</code></pre><p>Then visit your domain:</p>
<pre><code class="language-text">https://app.example.com</code></pre><p>If it opens, the main process is fine.</p>
<p>If it doesn&#39;t open, prioritize checking these points:</p>
<ul>
<li>Is the local service still alive?</li>
<li>Is the <code>hostname</code> written incorrectly?</li>
<li>Has the Cloudflare DNS been pointed to the Tunnel?</li>
<li>Is the server time completely off?</li>
<li>Is the configuration file path correct?</li>
</ul>
<h2 id="set-up-auto-start-on-boot">Set up Auto-Start on Boot</h2><p>After confirming that running manually is fine, install it as a systemd service:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> cloudflared service install
<span class="hljs-built_in">sudo</span> systemctl <span class="hljs-built_in">enable</span> --now cloudflared</code></pre><p>Check the status:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> systemctl status cloudflared</code></pre><p>View real-time logs:</p>
<pre><code class="language-bash">journalctl -u cloudflared -f</code></pre><p>If you can see a successful connection in the logs and the domain is accessible, you&#39;re pretty much done.</p>
<h2 id="common-commands">Common Commands</h2><table>
<thead>
<tr>
<th align="left">Function</th>
<th align="left">Command</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Log in to account</td>
<td align="left"><code>cloudflared tunnel login</code></td>
</tr>
<tr>
<td align="left">Create a tunnel</td>
<td align="left"><code>cloudflared tunnel create &lt;name&gt;</code></td>
</tr>
<tr>
<td align="left">View tunnels</td>
<td align="left"><code>cloudflared tunnel list</code></td>
</tr>
<tr>
<td align="left">Delete a tunnel</td>
<td align="left"><code>cloudflared tunnel delete &lt;name&gt;</code></td>
</tr>
<tr>
<td align="left">Bind a domain</td>
<td align="left"><code>cloudflared tunnel route dns &lt;name&gt; &lt;domain&gt;</code></td>
</tr>
<tr>
<td align="left">Validate config</td>
<td align="left"><code>cloudflared tunnel ingress validate</code></td>
</tr>
<tr>
<td align="left">Run in foreground</td>
<td align="left"><code>cloudflared tunnel run &lt;name&gt;</code></td>
</tr>
<tr>
<td align="left">View service</td>
<td align="left"><code>sudo systemctl status cloudflared</code></td>
</tr>
<tr>
<td align="left">View logs</td>
<td align="left"><code>journalctl -u cloudflared -f</code></td>
</tr>
</tbody></table>
<h2 id="some-security-advice">Some Security Advice</h2><p>Tunnel does not mean you can leave everything unprotected.</p>
<p>If it&#39;s an admin panel, it&#39;s best to add another layer of login, or use Cloudflare Access to restrict access. Otherwise, even if the port isn&#39;t exposed, the service itself might still get scanned.</p>
<p>Simply put: treat anything accessible from the public network as a public service. Don&#39;t take chances.</p>
]]></content>
  </entry>
  <entry>
    <title>WireGuard Minecraft Multiplayer Guide</title>
    <link href="https://blog.kauriss.site/en/2025/11/21/WireGuard-Minecraft-%E8%81%94%E6%9C%BA%E6%95%99%E7%A8%8B/" />
    <id>https://blog.kauriss.site/en/2025/11/21/WireGuard-Minecraft-%E8%81%94%E6%9C%BA%E6%95%99%E7%A8%8B/</id>
    <updated>2025-11-21T00:00:00.000Z</updated>
    <summary>Use WireGuard and a VPS to turn your home Minecraft server into one your friends can connect to.</summary>
    <content type="html"><![CDATA[<p>When hosting a Minecraft server on your own computer, the most annoying part isn&#39;t starting the server—it&#39;s getting your friends to connect.</p>
<p>Without a public IP, not knowing how to configure port forwarding, and ISPs potentially blocking ports... you spend ages messing around, and your friends still can&#39;t join. It&#39;s pretty frustrating.</p>
<p>I solve this using WireGuard + a VPS. The core idea is: put your home computer and the VPS into the same virtual local area network (VLAN) first, and then let players connect through the VPS or a virtual IP.</p>
<p><img src="/images/WireGuard-Logo.svg" alt=""></p>
<h2 id="use-case">Use Case</h2><p>This guide is for when you are running a Minecraft server at home or in a dorm, but your friends can&#39;t connect from the outside network.</p>
<ul>
<li>You don&#39;t have a public IP, or your public IP is unstable.</li>
<li>You don&#39;t want to deal with complex manual port forwarding on your router.</li>
<li>You have a VPS with internet access.</li>
<li>You are okay with Minecraft traffic being relayed through the VPS.</li>
</ul>
<p>If you already have a public IP and port forwarding works reliably, you don&#39;t need WireGuard.</p>
<h2 id="two-solutions">Two Solutions</h2><p>Look at your needs first. Don&#39;t just force everyone to install WireGuard right away.</p>
<table>
<thead>
<tr>
<th align="left">Solution</th>
<th align="left">Who installs WireGuard?</th>
<th align="left">Best for</th>
<th align="left">Trade-offs</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Solution A: Public Forwarding</td>
<td align="left">Only the server host</td>
<td align="left">When you want friends to connect directly to the VPS IP</td>
<td align="left">All traffic goes through the VPS; latency might be slightly higher</td>
</tr>
<tr>
<td align="left">Solution B: Full Mesh Network</td>
<td align="left">Host and all players</td>
<td align="left">A small, long-term group of friends wanting a LAN-like experience</td>
<td align="left">Everyone has to configure a client</td>
</tr>
</tbody></table>
<p>For beginners, I recommend starting with Solution A. Get it running first; don&#39;t overcomplicate things from the start.</p>
<h2 id="prerequisites">Prerequisites</h2><p>You will need:</p>
<ol>
<li>A VPS with a public IP. Debian/Ubuntu is the easiest to use.</li>
<li>The WireGuard client installed on the host computer.</li>
<li>VPS firewall and cloud provider security groups configured to allow:<ul>
<li>UDP <code>51820</code></li>
<li>TCP <code>25565</code></li>
</ul>
</li>
<li>A Minecraft server that runs normally on the host computer.</li>
</ol>
<p>If you can&#39;t even connect to Minecraft locally, fix Minecraft first. Don&#39;t suspect WireGuard yet.</p>
<h2 id="final-result">Final Result</h2><p>Once finished, friends can connect to your Minecraft server via the VPS entry point. The host computer and the VPS will form a virtual local network using WireGuard, and the VPS will forward external player connections to your home server.</p>
<p>If you use Solution B, players will also join the same WireGuard network, creating an experience closer to a LAN multiplayer game.</p>
<h2 id="installing-wireguard-on-the-vps">Installing WireGuard on the VPS</h2><p>Run this on your VPS:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> apt update
<span class="hljs-built_in">sudo</span> apt install wireguard -y</code></pre><p>Generate the server keys:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> <span class="hljs-built_in">mkdir</span> -p /etc/wireguard
<span class="hljs-built_in">cd</span> /etc/wireguard
wg genkey | <span class="hljs-built_in">sudo</span> <span class="hljs-built_in">tee</span> privatekey | wg pubkey | <span class="hljs-built_in">sudo</span> <span class="hljs-built_in">tee</span> publickey</code></pre><p>View the public key:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> <span class="hljs-built_in">cat</span> /etc/wireguard/publickey</code></pre><p>Do not share the private key. You can share the public key.</p>
<p>Enable IP forwarding:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> sysctl -w net.ipv4.ip_forward=1
<span class="hljs-built_in">sudo</span> sed -i <span class="hljs-string">&#x27;/net.ipv4.ip_forward=1/s/^#//&#x27;</span> /etc/sysctl.conf
<span class="hljs-built_in">sudo</span> sysctl -p</code></pre><h2 id="solution-a-public-forwarding">Solution A: Public Forwarding</h2><p>In this solution, your friends do not need to install WireGuard. They just connect directly to <code>VPS公网IP:25565</code>.</p>
<h3 id="1.-generate-keys-on-the-host-computer">1. Generate Keys on the Host Computer</h3><p>Open the WireGuard client and add an empty tunnel. The client will automatically generate a private key and a public key.</p>
<p>You only need to copy the host computer&#39;s public key to paste into the VPS configuration later.</p>
<h3 id="2.-configure-the-vps">2. Configure the VPS</h3><p>Edit the configuration file:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> nano /etc/wireguard/wg0.conf</code></pre><p>Example:</p>
<pre><code class="language-ini"><span class="hljs-section">[Interface]</span>
<span class="hljs-attr">Address</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.1</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">ListenPort</span> = <span class="hljs-number">51820</span>
<span class="hljs-attr">PrivateKey</span> = &lt;VPS私钥&gt;

<span class="hljs-attr">PostUp</span> = iptables -A FORWARD -i wg0 -j ACCEPT<span class="hljs-comment">; iptables -t nat -A PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.0.0.2</span>
<span class="hljs-attr">PostDown</span> = iptables -D FORWARD -i wg0 -j ACCEPT<span class="hljs-comment">; iptables -t nat -D PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.0.0.2</span>

<span class="hljs-section">[Peer]</span>
<span class="hljs-attr">PublicKey</span> = &lt;服主电脑公钥&gt;
<span class="hljs-attr">AllowedIPs</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.2</span>/<span class="hljs-number">32</span></code></pre><p><code>10.0.0.2</code> is the host computer&#39;s virtual IP inside WireGuard.</p>
<h3 id="3.-configure-the-host-computer">3. Configure the Host Computer</h3><p>Fill this into the WireGuard client:</p>
<pre><code class="language-ini"><span class="hljs-section">[Interface]</span>
<span class="hljs-attr">PrivateKey</span> = &lt;服主电脑私钥&gt;
<span class="hljs-attr">Address</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.2</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">DNS</span> = <span class="hljs-number">1.1</span>.<span class="hljs-number">1.1</span>

<span class="hljs-section">[Peer]</span>
<span class="hljs-attr">PublicKey</span> = &lt;VPS公钥&gt;
<span class="hljs-attr">Endpoint</span> = &lt;VPS公网IP&gt;:<span class="hljs-number">51820</span>
<span class="hljs-attr">AllowedIPs</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.0</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">PersistentKeepalive</span> = <span class="hljs-number">25</span></code></pre><p>The <code>PrivateKey</code> here is usually already filled in by the client. Don&#39;t accidentally overwrite it with the wrong one.</p>
<h3 id="4.-start-and-test">4. Start and Test</h3><p>Start it on the VPS:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> wg-quick up wg0
<span class="hljs-built_in">sudo</span> systemctl <span class="hljs-built_in">enable</span> wg-quick@wg0</code></pre><p>Start WireGuard on the host computer.</p>
<p>Then test it on the host computer:</p>
<pre><code class="language-bash">ping 10.0.0.1</code></pre><p>If the ping goes through, the tunnel is connected.</p>
<p>Next, start the Minecraft server. Have your friends connect to:</p>
<pre><code class="language-text">&lt;VPS公网IP&gt;:25565</code></pre><p>If they can&#39;t connect, check if <code>25565</code> is allowed on the VPS first, and then check if the local firewall is blocking Java.</p>
<h2 id="solution-b-full-mesh-network">Solution B: Full Mesh Network</h2><p>This solution acts more like a true virtual local network. Everyone gets a <code>10.0.0.x</code> address.</p>
<p>This is best for a steady group of friends playing long-term. The downside is obvious: everyone has to install the client, and if even one config is wrong, they won&#39;t be able to connect.</p>
<h3 id="1.-collect-everyones-public-keys">1. Collect Everyone&#39;s Public Keys</h3><p>Have every player install WireGuard, add an empty tunnel, and send their public key to you.</p>
<p>Assign an IP to everyone:</p>
<table>
<thead>
<tr>
<th align="left">Person</th>
<th align="left">Virtual IP</th>
</tr>
</thead>
<tbody><tr>
<td align="left">VPS</td>
<td align="left"><code>10.0.0.1</code></td>
</tr>
<tr>
<td align="left">Server Host</td>
<td align="left"><code>10.0.0.2</code></td>
</tr>
<tr>
<td align="left">Player A</td>
<td align="left"><code>10.0.0.3</code></td>
</tr>
<tr>
<td align="left">Player B</td>
<td align="left"><code>10.0.0.4</code></td>
</tr>
</tbody></table>
<p>Do not overlap IPs. If they overlap, it&#39;s game over.</p>
<h3 id="2.-vps-configuration">2. VPS Configuration</h3><pre><code class="language-ini"><span class="hljs-section">[Interface]</span>
<span class="hljs-attr">Address</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.1</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">ListenPort</span> = <span class="hljs-number">51820</span>
<span class="hljs-attr">PrivateKey</span> = &lt;VPS私钥&gt;

<span class="hljs-section">[Peer]</span>
<span class="hljs-attr">PublicKey</span> = &lt;服主公钥&gt;
<span class="hljs-attr">AllowedIPs</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.2</span>/<span class="hljs-number">32</span>

<span class="hljs-section">[Peer]</span>
<span class="hljs-attr">PublicKey</span> = &lt;玩家A公钥&gt;
<span class="hljs-attr">AllowedIPs</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.3</span>/<span class="hljs-number">32</span>

<span class="hljs-section">[Peer]</span>
<span class="hljs-attr">PublicKey</span> = &lt;玩家B公钥&gt;
<span class="hljs-attr">AllowedIPs</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.4</span>/<span class="hljs-number">32</span></code></pre><p>For every new person, add another <code>[Peer]</code>.</p>
<h3 id="3.-player-client-configuration">3. Player Client Configuration</h3><p>Everyone&#39;s configuration looks similar, just with different private keys and addresses.</p>
<p>Server Host:</p>
<pre><code class="language-ini"><span class="hljs-section">[Interface]</span>
<span class="hljs-attr">PrivateKey</span> = &lt;服主自己的私钥&gt;
<span class="hljs-attr">Address</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.2</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">DNS</span> = <span class="hljs-number">1.1</span>.<span class="hljs-number">1.1</span>

<span class="hljs-section">[Peer]</span>
<span class="hljs-attr">PublicKey</span> = &lt;VPS公钥&gt;
<span class="hljs-attr">Endpoint</span> = &lt;VPS公网IP&gt;:<span class="hljs-number">51820</span>
<span class="hljs-attr">AllowedIPs</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.0</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">PersistentKeepalive</span> = <span class="hljs-number">25</span></code></pre><p>Player A:</p>
<pre><code class="language-ini"><span class="hljs-section">[Interface]</span>
<span class="hljs-attr">PrivateKey</span> = &lt;玩家A自己的私钥&gt;
<span class="hljs-attr">Address</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.3</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">DNS</span> = <span class="hljs-number">1.1</span>.<span class="hljs-number">1.1</span>

<span class="hljs-section">[Peer]</span>
<span class="hljs-attr">PublicKey</span> = &lt;VPS公钥&gt;
<span class="hljs-attr">Endpoint</span> = &lt;VPS公网IP&gt;:<span class="hljs-number">51820</span>
<span class="hljs-attr">AllowedIPs</span> = <span class="hljs-number">10.0</span>.<span class="hljs-number">0.0</span>/<span class="hljs-number">24</span>
<span class="hljs-attr">PersistentKeepalive</span> = <span class="hljs-number">25</span></code></pre><p>The private key must belong to that specific person. Do not just mass-send the exact same config file; it won&#39;t work.</p>
<h3 id="4.-connecting-to-the-game">4. Connecting to the Game</h3><p>Start on VPS:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> wg-quick up wg0
<span class="hljs-built_in">sudo</span> systemctl <span class="hljs-built_in">enable</span> wg-quick@wg0</code></pre><p>After everyone starts WireGuard, ping each other:</p>
<pre><code class="language-bash">ping 10.0.0.1
ping 10.0.0.2</code></pre><p>If the ping goes through, players can just connect to the host&#39;s virtual IP:</p>
<pre><code class="language-text">10.0.0.2:25565</code></pre><h2 id="troubleshooting">Troubleshooting</h2><p>A few common issues:</p>
<table>
<thead>
<tr>
<th align="left">Issue</th>
<th align="left">Where to look first</th>
</tr>
</thead>
<tbody><tr>
<td align="left">WireGuard isn&#39;t handshaking</td>
<td align="left">Public Key, Endpoint, UDP 51820, Security Group</td>
</tr>
<tr>
<td align="left">Handshake successful but can&#39;t ping</td>
<td align="left"><code>AllowedIPs</code>, IP forwarding, Firewalls</td>
</tr>
<tr>
<td align="left">Can ping but can&#39;t enter the server</td>
<td align="left">Is Minecraft running? TCP 25565, Local firewall</td>
</tr>
<tr>
<td align="left">Solution A players can&#39;t connect</td>
<td align="left">VPS port forwarding rules, Cloud provider security group</td>
</tr>
</tbody></table>
<p>Check WireGuard status:</p>
<pre><code class="language-bash"><span class="hljs-built_in">sudo</span> wg</code></pre><p>You are only truly connected if you see a <code>latest handshake</code>. If there&#39;s no handshake, don&#39;t even look at Minecraft; the problem is still at the network layer.</p>
<h2 id="final-thoughts">Final Thoughts</h2><p>My personal recommendations:</p>
<ul>
<li>Temporary server: Solution A.</li>
<li>Fixed group of friends playing long-term: Solution B.</li>
<li>Don&#39;t want to explain configs to everyone: Solution A.</li>
<li>Want lower latency and better privacy: Solution B.</li>
</ul>
<p>Get it running first, then optimize. If you try to make everything perfect from the start when dealing with networks, you&#39;ll likely get stuck on a really stupid config error.</p>
]]></content>
  </entry>
</feed>