misOrz

IT Tech & Life

Caddy vs Nginx:自架服務該選哪一個?

Caddy vs Nginx: Which One Should You Pick for Self-Hosted Services?

If you have ever been woken up by an expired certificate, you already know what this comparison is really about. Caddy and Nginx do the same job: they sit in front of your services, terminate TLS, and route traffic. The difference is how much of that work they do for you.

What Is Caddy?

Caddy is an open-source web server written in Go, first released in 2015. It was the first web server to enable HTTPS automatically by default, and it now has over 75,000 stars on GitHub. Its headline feature is automatic HTTPS: write a domain name in the config file, and Caddy obtains a certificate from Let's Encrypt or ZeroSSL, renews it before expiry, and enables HTTP/2 and HTTP/3 — with no extra tooling.

What Is Nginx?

Nginx is an open-source web server written in C, first released in 2004. It is event-driven, famously light on memory, and remains one of the most widely deployed web servers and reverse proxies in the world. If you have used the internet, you have used Nginx. Its configuration is powerful and precise — and, frankly, more verbose.

Side-by-Side Comparison

Feature Caddy Nginx
Config difficulty Low, a few lines of Caddyfile Medium to high, detailed syntax
Automatic HTTPS Built in, zero setup No, needs Certbot or similar
Certificate renewal Automatic, handled internally Via Certbot cron / systemd timer
Config hot update Admin API, no reload needed nginx -s reload, graceful, keeps connections
HTTP/3 Enabled by default Experimental since 1.25, needs a build flag
Static file performance Fast, close behind Nginx Slightly ahead in raw benchmarks
Memory footprint Moderate (Go runtime) Very low
Modules / ecosystem Plugins via xcaddy (compile time) Huge ecosystem, dynamic modules
Config format Caddyfile, JSON, API nginx.conf
Enterprise adoption Growing Extremely high
Chinese learning resources Fewer Abundant

Certificates: The Question That Started This Article

Can Nginx renew certificates by itself? No. Nginx only reads certificate files from disk (ssl_certificate and ssl_certificate_key). It has no ACME client, so it cannot talk to Let's Encrypt at all.

The standard setup is Certbot plus a scheduled renewal:

certbot --nginx -d example.com
certbot renew --dry-run   # test that auto-renewal works

Certbot installs a systemd timer that checks twice a day and renews any certificate with less than 30 days left, then reloads Nginx. Let's Encrypt certificates last 90 days, so this automation is not optional — it is the whole point.

A lighter alternative is acme.sh, which also supports DNS API verification, useful when port 80 is not reachable from the internet.

Caddy, by contrast, does all of this internally. There is no Certbot, no cron job, and no renewal to forget. That is the core of its appeal.

Reverse Proxy: Three Lines vs a Server Block

Caddy — put api.example.com in front of a FastAPI app on port 8000:

api.example.com {
    reverse_proxy localhost:8000
}

Nginx — the same job:

server {
    listen 443 ssl;
    server_name api.example.com;
    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8000;
    }
}

Both work. One of them also needed a certificate before it could start.

Docker

With Docker Compose, Caddy is one more service next to your app, with a named volume for its certificate store — that volume must persist, or every container rebuild will reissue certificates and hit rate limits:

services:
  api:
    build: .
    expose:
      - "8000"
  caddy:
    image: caddy:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
volumes:
  caddy_data:
  caddy_config:

The equivalent Nginx setup needs the same compose file plus a Certbot container or a host-side renewal job.

Which One Should You Pick?

Pick Caddy if you self-host services (Vaultwarden, n8n, Nextcloud, a FastAPI backend), you want HTTPS without thinking about it, or you have been bitten by an expired certificate before. For a personal server, it is the faster path to a secure setup.

Pick Nginx if your team already knows it deeply, you need very fine-grained traffic control (complex rewrites, rate limiting, multi-layer caching), or you are running at a scale where its raw throughput and ecosystem matter more than convenience.

You do not have to choose once and forever. Caddy ships a config adapter that converts Nginx configuration to Caddy format, and the two can run side by side on different domains or ports while you migrate one service at a time.

Verdict

Nginx is the industry default for a reason, and it is not going anywhere. But for self-hosted services, Caddy removes the single most annoying chore in the whole stack — certificate management — and that alone is worth switching for. The moment your first certificate renews itself without you noticing, you will not want to go back.


Caddy vs Nginx:自架服務該選哪一個?

如果你曾經因為憑證過期半夜被叫起來,你就知道這篇比較真正在比的是什麼。Caddy 和 Nginx 做的是同一份工作:擋在你的服務前面,處理 TLS、轉發流量。差別在於,它們幫你做了多少。

什麼是 Caddy?

Caddy 是用 Go 寫的開源網頁伺服器,2015 年首次發佈,是史上第一個預設就自動啟用 HTTPS 的網頁伺服器,GitHub 上已有超過 75,000 顆星。它的最大賣點就是自動 HTTPS:設定檔寫上網域名稱,它就自動向 Let's Encrypt 或 ZeroSSL 申請憑證、到期前自動續期、自動啟用 HTTP/2 與 HTTP/3,不需要另外裝任何工具。

什麼是 Nginx?

Nginx 是用 C 寫的開源網頁伺服器,2004 年首次發佈,事件驅動、記憶體佔用極低,至今仍是全球最普及的網頁伺服器與反向代理之一。你用過網路,就用過 Nginx。它的設定功能強大精細,相對也比較囉嗦。

並排比較

比較項目 Caddy Nginx
設定難度 低,Caddyfile 幾行搞定 中高,語法細碎
自動 HTTPS 內建,零設定 沒有,需搭配 Certbot
憑證續期 內部自動處理 靠 Certbot cron/systemd timer
設定熱更新 管理 API 推送,免重啟 nginx -s reload,優雅重載、不斷連線
HTTP/3 預設支援 1.25 起實驗性支援,需編譯參數
靜態檔案效能 快,略遜於 Nginx 原始效能測試略勝一籌
記憶體佔用 中等(Go runtime) 極低
模組/生態系 外掛需 xcaddy 編譯期加入 生態系龐大,支援動態模組
設定格式 Caddyfile、JSON、API nginx.conf
企業普及度 持續成長中 極高
中文學習資源 較少 極多

憑證:這篇文章的起點

Nginx 能自己續憑證嗎?不行。Nginx 只會讀取磁碟上的憑證檔案(ssl_certificate 與 ssl_certificate_key),它本身沒有 ACME 客戶端,完全不會去跟 Let's Encrypt 打交道。

標準做法是 Certbot 加上排程續期:

certbot --nginx -d example.com
certbot renew --dry-run   # 測試自動續期是否正常

Certbot 會裝一個 systemd timer,每天檢查兩次,憑證剩下不到 30 天就自動續期,然後重載 Nginx。Let's Encrypt 憑證效期只有 90 天,所以自動化不是加分,是必要。

比較輕量的替代方案是 acme.sh,還支援 DNS API 驗證,80 埠不對外開放也能簽憑證。

Caddy 則是把這整件事都內建了。沒有 Certbot、沒有 cron、沒有會忘記的續期。這就是它最大的吸引力。

反向代理:三行 vs 一個 server 區塊

Caddy——把 api.example.com 轉給本機 8000 埠的 FastAPI:

api.example.com {
    reverse_proxy localhost:8000
}

Nginx——同樣的工作:

server {
    listen 443 ssl;
    server_name api.example.com;
    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8000;
    }
}

兩個都能用,但其中一個在啟動之前,還得先有一張憑證。

Docker

用 Docker Compose 時,Caddy 就是你 App 旁邊多一個服務,憑證儲存一定要用具名 volume 持久化,否則每次重建容器都要重新簽發憑證,會撞上簽發速率限制:

services:
  api:
    build: .
    expose:
      - "8000"
  caddy:
    image: caddy:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
volumes:
  caddy_data:
  caddy_config:

同樣的架構換成 Nginx,除了 Compose 檔,還得再加一個 Certbot 容器或主機上的續期排程。

該選哪一個?

選 Caddy,如果你自架服務(Vaultwarden、n8n、Nextcloud、FastAPI 後端都算),想要 HTTPS 但不想管憑證,或你曾經被過期憑證搞過。對個人伺服器來說,它是更快抵達安全上線的路。

選 Nginx,如果團隊對它已經很熟,需要非常細緻的流量控制(複雜 rewrite、限流、多層 cache),或規模大到原始吞吐量與生態系比方便性更重要。

不用一次定生死。Caddy 內建 config adapter,可以把 Nginx 設定轉成 Caddy 格式,兩個也可以先在不同網域或 port 上並存,一個服務一個服務慢慢搬。

結論

Nginx 是業界預設選擇,有它的道理,也不會消失。但對自架服務來說,Caddy 拿掉了整個架構裡最煩的一件事——憑證管理,光是這一點就值得換。等你第一次發現憑證自己續好了、你完全沒注意到,那一刻你就回不去了。

NixOS vs Ubuntu:差異比較與適合的使用者

一句話定位

很多人問我該選 NixOS 還是 Ubuntu。先講結論:這兩個系統的哲學完全不同,選哪個取決於你想怎麼「管」一台電腦,而不是哪個比較強。

  • Ubuntu:最成熟、最好上手的通用 Linux 發行版,裝好就能用。
  • NixOS:整個系統用一份設定檔宣告出來的 Linux,改設定、升級、回滾全部可重現。

核心差異對照

項目UbuntuNixOS
系統設定方式各處設定檔+指令,改完就生效全部寫進 configuration.nix,改完要 nixos-rebuild switch 才生效
套件管理apt(.deb)+ SnapNix(nixpkgs,超過 8 萬個套件)
升級與回滾升級出問題要手動修復原子升級,開機選單可直接回到上一個世代(generation)
套件隔離全域共用函式庫,依賴衝突較常見每個套件獨立放在 /nix/store,多版本可共存
發行週期半年一版、兩年一個 LTS,LTS 標準支援 5 年(26.04 LTS 支援到 2031 年 5 月)每半年一版(目前最新 26.05),單一版本支援約 7 個月
學習曲線低,文件與教學最多高,要學 Nix 語言與模組系統
硬體與商業軟體最好,筆電驅動、閉源軟體支援最完整較弱,閉源驅動與商業軟體要額外處理
商業支援Canonical 提供 Ubuntu Pro、企業支援與認證以社群為主,無同等級商業支援

NixOS 最大的不同:系統即設定檔

NixOS logo

NixOS 標誌,CC BY 4.0 授權(出處:NixOS 官方品牌頁,授權條款)

在 NixOS,裝了什麼軟體、開了什麼服務、系統長什麼樣子,全部寫在設定檔裡。換一台機器時,把設定檔複製過去、跑一次重建,就能得到幾乎一樣的系統;設定檔還能放進 Git 版控,哪天改壞了直接回到上一個版本。這也是為什麼它在開發環境與伺服器佈署上特別受歡迎。

代價就是門檻:Nix 語言與模組寫法一開始不好上手,遇到問題時,網路上的解法也比 Ubuntu 少很多。

Ubuntu 最大的優勢:生態與省心

Ubuntu logo

Ubuntu Circle of Friends 標誌 © Canonical Ltd.(GPL-3.0);Ubuntu 為 Canonical Ltd. 的註冊商標

Ubuntu 是最多人用的 Linux 桌面與伺服器發行版之一,遇到問題幾乎都找得到現成答案。筆電 Wi-Fi、顯示卡驅動、印表機、商業軟體的相容性通常是最好的;企業環境還能買 Canonical 的支援與 Ubuntu Pro 的延長安全更新。缺點是系統狀態是「長」出來的:裝久了、改多了,出問題時很難說清楚當初改了什麼,也很難完整複製到另一台機器。

適合的使用者

選 NixOS,如果你是:

  • 開發者,想讓開發環境完全可重現,換機器不用重配
  • SRE/DevOps,想把伺服器設定版控化、多台機器保持一致
  • 喜歡折騰、願意花時間學新工具,換取長期維護的輕鬆
  • 需要同一軟體多版本共存(例如多個 Python、Node 版本互不干擾)

選 Ubuntu,如果你是:

  • Linux 新手,或只想裝好就專心工作
  • 企業、生產環境,需要商業支援、認證與長期安全更新
  • 筆電主力機,硬體相容與閉源驅動優先
  • 團隊協作,需要大家用同一套標準環境

折衷方案

不一定要二選一:在 Ubuntu 上也可以只裝 Nix 套件管理器,享受宣告式開發環境(home-manager、flake),系統本體仍維持 Ubuntu 的省心;反過來也可以先在虛擬機裡玩 NixOS,習慣了再決定要不要當主力。

結語

想要「裝好就不用管」選 Ubuntu;想要「系統狀態全部寫在設定檔裡、可以重現」選 NixOS。我的建議是:主力工作機先用 Ubuntu,需要可重現環境時再把 Nix 引進來,兩邊的好處都拿得到。

xtool: build and deploy iOS apps without Xcode

Found this while looking into iOS build options: xtool is a cross-platform Xcode replacement — it builds a SwiftPM package into an iOS app on Linux, WSL, macOS, and Windows.

What it covers:

  • Build a SwiftPM package into an iOS app
  • Sign and install iOS apps (xtool install xxx.ipa, xtool devices, xtool launch)
  • Apple Developer Services via CLI (xtool auth, xtool ds) and as a library (XKit SwiftPM product)

Handy subcommands:

xtool new      # create a new xtool SwiftPM project
xtool dev      # build and run it (screenshot below: Ubuntu + VSCode → iPhone)
xtool devices  # list devices

Why I bookmarked it: my day-to-day dev machine is Linux/Android. If I ever want an iOS companion for my QRCode scan app without buying into the full Xcode workflow, this is the kind of tool that makes it plausible — SwiftPM in, signed ipa out, no Mac required (per its README).

Screenshot from the xtool repo (VSCode on Ubuntu running xtool dev → installing to an iPhone):

Screenshot of xtool running in VSCode on Ubuntu, installing to an iPhone
Screenshot from the xtool repo — © 2024 Kabir Oberai, MIT License


Reference: https://github.com/xtool-org/xtool

Fix TTS init failure on Xiaomi (MIUI): declare TTS_SERVICE in the AndroidManifest queries block

Symptom: In my QRCode scan app, TextToSpeech init failed on a Xiaomi phone (China MIUI ROM) — status=-1 for engine com.google.android.tts, completely silent. The same APK worked fine on a Realme phone.

What I ruled out first:

  • Media volume was on, in-app TTS toggle was on.
  • Xiaomi's "Text-to-speech output": preferred engine was Google TTS, language Chinese (Taiwan), and the system "Listen to an example" test played sound fine — the engine itself was healthy.
  • Explicitly specifying Google TTS and adding an engine fallback list both failed the same way (system default was already Google, nothing to fall back to).

Root cause: Android 11+ package visibility. Since API 30, an app cannot see — and therefore cannot bind — another app's service unless it declares the intent in <queries>. Google TTS is a third-party app providing a TTS service; without the declaration, binding the TTS engine is silently blocked and TextToSpeech init returns ERROR (-1). Some OEM ROMs (e.g. Realme) don't enforce this strictly, which is why the same APK worked there.

Fix — add to AndroidManifest.xml:

<queries>
    <intent>
        <action android:name="android.intent.action.TTS_SERVICE" />
    </intent>
</queries>

After adding this (shipped as v1.1), TTS worked on both Xiaomi and Realme.

Lesson: whenever your app binds a service provided by another app (TTS engine, etc.) on Android 11+, declare the matching <queries> entry — otherwise the bind fails silently on strict OEM ROMs.


 

Create VLANs in Linux

 

ip link add link eth0 name eth0.3001 type vlan id 3001

ip addr add 192.168.100.1/24 brd 192.168.100.255 dev eth0.3001

ip link set dev eth0.3001 up

ip -d addr show



Reference: https://www.baeldung.com/linux/vlans-create





Login as Root in Ubuntu GUI

GDM – GNOME Display Manager

/etc/pam.d/gdm-autologin

/etc/pam.d/gdm-password

註解掉 # auth required pam_succeed_if.so user != root quiet_success

sudo service gdm restart





How to connect hidden ssid on debian 11

root@LINUX:~# rfkill unblock wifi

root@LINUX:~# killall wpa_supplicant

root@LINUX:~# wpa_passphrase SSID-NAME Password > wpa.conf

root@LINUX:~# wpa_supplicant -i wlan0 -c wpa.conf -B

root@LINUX:~# dhclient wlan0