WindowsをLinuxに変えて、VPNで繋げてみた
大学の時に使っていたDELLのノートPCが、Windows 10のまま眠っていた。
メモリは8GB。Windows 11にしても重くなるだけだし、かといって捨てるのはもったいない。
だったら Ubuntu Serverを入れて、自宅のバックエンド実験サーバーにしてしまおう、というのがこの記事の話。
メインのMacに開発ツールやDBを全部入れると、環境がぐちゃぐちゃになる。
それを避けるために、古いPCをLinuxサーバー化して「壊してもいい箱」として運用する。
VPN(Tailscale)で外からも安全に触れるようにして、Docker上にPostgreSQLなどの実験環境を立てるところまでをまとめた。
Part 1:Ubuntu Serverのインストールと初期設定
なぜWindows → Ubuntu Server なのか
サーバー用途で旧PCを再利用するなら、Ubuntu Serverが合理的な選択肢になる。
- 軽い:GUIがないぶんリソース消費が小さく、8GB RAMでも余裕で動く
- アップデートを自分で制御できる:Windowsの強制アップデートによる事故がない
- リモート管理が前提の設計:SSHで完結する
- バックエンドとの相性が良い:PostgreSQL、Python、Dockerなどが自然に動く
ブータブルUSBの作成
インストールにはUSBメモリ(8GB以上)が必要。
- ubuntu.com から Ubuntu Server 24.04 LTS のISOをダウンロード
- balenaEtcher(etcher.balena.io)でISOをUSBに書き込む
- USBの中身は上書きされるので、必要なデータは事前にバックアップ
BIOS設定の変更
DELLの場合、起動時に F2連打 でBIOSに入れる。
- Secure Boot →
Audit Mode(実質Disabled)に変更 - Boot Order → USBを最優先に設定
- 保存して再起動
インストール時の設定ポイント
| 項目 | 設定値 |
|---|---|
| 言語 | English(Server版は日本語を選べない) |
| ストレージ | Use an entire disk |
| LVM | 有効 |
| 暗号化(LUKS) | 無効 |
| Ubuntu Pro | Skip |
| OpenSSH Server | インストール必須 |
サーバー名とユーザー名は任意のものを設定する。
インストール完了後、「Reboot Now」でUSBを抜いて再起動。
ネットワーク設定:最初の「鶏と卵」問題
Ubuntu Serverをインストールした直後、Wi-Fiを使うには wpasupplicant が必要だが、そのインストールにはネット接続が要るという問題にぶつかる。
解決策はシンプルで、まず有線LANでルーターに直結する。
① 有線LANをnetplanで有効化
sudo nano /etc/netplan/01-netcfg.yaml
network:
version: 2
renderer: networkd
ethernets:
enp1s0: # ← デバイス名は `ip a` で確認
dhcp4: true
dhcp6: false
sudo chmod 600 /etc/netplan/01-netcfg.yaml
sudo netplan apply
ip a でIPv4アドレスが取得できていればOK。
② wpasupplicantをインストールしてWi-Fiを設定
sudo apt update && sudo apt install -y wpasupplicant
同じnetplanファイルにWi-Fiの設定を追加する。
wifis:
wlo1: # ← デバイス名は `ip a` で確認
dhcp4: true
access-points:
"<SSID名>":
password: "<パスワード>"
sudo netplan apply
Wi-FiでIPが取れたら、有線を抜いても大丈夫。
注意:netplanのYAMLはインデントに厳しい。スペースがずれるとエラーになる。また、設定ファイルは
chmod 600でパーミッションを絞っておくこと。
SSH接続と鍵認証
サーバーのIPが分かったら、別のPC(Macなど)からSSHで入る。
ssh <ユーザー名>@<サーバーのローカルIP>
毎回パスワードを打つのは面倒なので、鍵認証に切り替える。
# クライアント側で鍵を生成
ssh-keygen -t ed25519 -C "my-server"
# 公開鍵をサーバーに送る
ssh-copy-id <ユーザー名>@<サーバーのローカルIP>
Macの場合はキーチェーンに登録しておくと便利。
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
ノートPCサーバーの落とし穴:蓋を閉じるとスリープする
ノートPCをサーバー転用すると、蓋を閉じた瞬間にスリープして接続が切れる。
/etc/systemd/logind.conf を編集して無効化する。
sudo nano /etc/systemd/logind.conf
以下の行のコメントを外して ignore に変更:
HandleLidSwitch=ignore
sudo systemctl restart systemd-logind
これでサーバーは蓋を閉じたまま稼働し続ける。
Part 2:Tailscale VPNで外から安全にアクセスする
VPNの2種類を整理する
「VPN」と聞くと、SurfsharkやNordVPNのような「IP秘匿サービス」を思い浮かべるかもしれないが、今回使いたいのはそれとは目的が違うVPN。
| 種類 | 目的 | イメージ |
|---|---|---|
| 商用VPN(Surfshark等) | IPアドレスの秘匿、地域制限の回避 | 変装して街を歩く |
| Tailscale | 自分のデバイス同士をプライベートにつなぐ | 自分の部屋同士を秘密の通路でつなぐ |
Tailscaleは後者。自宅にいなくても、カフェから自宅サーバーにまるで隣にあるかのようにアクセスできるようにするのが目的。
Tailscaleの仕組み
- WireGuardベースのメッシュ型VPN
- 中央サーバーを経由せず、デバイス同士がP2Pで直接通信するため高速
- インストールすると各デバイスに
100.x.y.zという専用のIPアドレスが付与される - この専用IP宛ての通信だけが暗号化トンネルを通る。普段のネット通信には影響しない
つまり、PCからサーバーの Tailscale IP にアクセスすれば暗号化された安全な経路を通り、通常のWebブラウジングはいつも通り。全通信を迂回させる商用VPNとはここが違う。
導入手順
サーバー側(Ubuntu)
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale status
tailscale up を実行するとログイン用のURLが表示されるので、ブラウザで開いて認証する。
クライアント側(Mac / Windows / スマホ)
公式サイトまたはApp Storeからアプリをインストールして、同じアカウントでログインするだけ。
これで、サーバーとクライアントが同じ Tailscale ネットワーク(tailnet)に入る。
おすすめの追加設定
MagicDNS
管理画面(login.tailscale.com/admin/dns)でMagicDNSを有効にすると、IPの代わりにホスト名でアクセスできるようになる。
Tailscale Funnel(一時的なWeb公開)
開発中のアプリを一時的に外部に見せたい場合:
sudo tailscale funnel 3000
Tailscaleを入れていない人でもブラウザでアクセスできるURLが発行される。
止めたい時は:
sudo tailscale funnel --reset
ただしFunnelはHTTPS(443)のみで帯域制限もあるため、数人に見せる程度の用途向き。本格的な公開にはCloudflare TunnelやVPSへの移管を検討する。
SSH接続をTailscale経由に切り替える
Tailscaleを導入したら、SSHの接続先をローカルIPからTailscale IPに変更する。
ssh <ユーザー名>@<サーバーのTailscale IP>
これで、自宅の外からでもサーバーにSSH接続できるようになる。
ルーターのポート開放は不要で、Tailscaleの暗号化トンネルを通るのでセキュリティも高い。
VSCode Remote-SSHとの組み合わせ
Tailscale + VSCode Remote-SSH の組み合わせは非常に快適。
- VSCodeに「Remote - SSH」拡張をインストール
Ctrl+Shift+P(MacならCmd+Shift+P)→ 「Remote-SSH: Connect to Host」<ユーザー名>@<サーバーのTailscale IP>を入力- 鍵認証でログイン
接続すると、VSCodeの拡張機能は自動的にローカル用とサーバー用に分かれる。
- ローカル側:テーマ、キーバインドなどUI系
- サーバー側:Python、Docker、GitLensなど開発系
つまり、手元のPCには「VSCodeとブラウザ」だけあれば開発できる状態になる。
Python、Node.js、Dockerなどのランタイムは全部サーバー側に置けばいい。
Part 3:Dockerで実験環境を作る
Dockerとは
Dockerは、アプリケーションとその実行環境(OS、ライブラリ、設定ファイルなど)をまとめて1つの 「コンテナ」 として動かす技術。
1台のサーバー上に、互いに干渉しない独立した環境を複数作れる。
DockerとPython仮想環境(venv)の違い
| 比較項目 | Docker | venv |
|---|---|---|
| 隔離のレベル | OS・ライブラリごと丸ごと | Pythonパッケージだけ |
| 用途 | 本番アプリ、DB、APIなど | 軽い実験、スクリプト |
| 重さ | やや重い | 軽い |
ちょっとした実験やスクリプトなら venv で十分。本番寄りのサービスや複数言語が絡むならDockerを使う。
コンテナ分割の考え方:「機能単位」で分ける
コンテナは「アプリ単位」ではなく、機能(役割)ごとに分けるのが原則。
たとえば「ダッシュボードアプリ」を作る場合:
- フロントエンド(Next.js)→ 1コンテナ
- バックエンドAPI(FastAPI)→ 1コンテナ
- データベース(PostgreSQL)→ 1コンテナ
こう分けるメリットは3つ。
- 障害の隔離:DBが壊れてもフロントは動く
- 個別アップデート:PostgreSQLだけバージョンを上げたい時、そのコンテナだけ差し替え
- 再利用:PostgreSQLは複数のアプリで共有できる
ポート番号の管理ルール
Dockerでは各コンテナにポートを割り当てて区別する。
ルールを決めておかないと衝突して事故るので、番号帯で分けるのがおすすめ。
| 番号帯 | 用途 |
|---|---|
| 3000番台 | フロントエンド(Next.js、Reactなど) |
| 5000番台 | 管理ツール(pgAdminなど) |
| 8000番台 | バックエンドAPI(FastAPI、Expressなど) |
| 9000番台 | モニタリング(Grafana、Prometheusなど) |
同じ種類のアプリが複数ある場合は、末尾をずらす(8000、8001、8002…)。
使えるポートは1024〜65535。1024未満はシステム予約なので避ける。
Dockerのインストール
sudo apt update
sudo apt install docker.io docker-compose-v2 -y
sudo usermod -aG docker $USER
usermod の後は一度ログアウトして再ログインが必要。
再ログイン後に groups コマンドで docker が含まれていることを確認する。
PostgreSQL + pgAdmin をDocker Composeで立てる
ディレクトリを作成
mkdir -p ~/docker/postgres
cd ~/docker/postgres
docker-compose.yml を作成
services:
db:
image: postgres:16
restart: always
environment:
POSTGRES_USER: <DBユーザー名>
POSTGRES_PASSWORD: <DBパスワード>
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
restart: always
environment:
PGADMIN_DEFAULT_EMAIL: <メールアドレス>
PGADMIN_DEFAULT_PASSWORD: <pgAdminパスワード>
ports:
- "5050:80"
volumes:
pgdata:
各項目の意味
| 項目 | 意味 |
|---|---|
image | 使用するDockerイメージ(Docker Hubから自動取得) |
restart: always | サーバー再起動時にコンテナも自動起動 |
environment | コンテナに渡す環境変数 |
ports: "5432:5432" | ホスト側ポート:コンテナ内ポート のマッピング |
volumes | データの永続化(コンテナを消してもデータが残る) |
起動・確認・停止
docker compose up -d # バックグラウンドで起動
docker compose ps # 起動中のコンテナ一覧
docker compose logs -f # ログをリアルタイム表示
docker compose down # 全コンテナ停止
pgAdminからPostgreSQLに接続する
ブラウザで http://<サーバーのTailscale IP>:5050 にアクセスし、設定したメールアドレスとパスワードでログイン。
「Add New Server」から以下を入力:
- Host:
db(docker-compose.yml内のサービス名で名前解決される) - Port:
5432 - Username / Password:docker-compose.ymlで設定した値
pgAdminはWebアプリなので、Tailscale経由ならスマホやiPadからでもアクセスできる。ただし画面はPC向けなので、スマホでは使いづらい。
データ永続化について
volumes で指定したデータは、コンテナを削除して作り直しても消えない。
ただし、PostgreSQLのメジャーバージョンを変える場合(例:16→17)はデータ形式が変わる可能性があるため、バックアップしてから移行すること。
次のステップ:pgvector
PostgreSQLにベクトル検索機能を追加したい場合は pgvector を導入する。
docker-compose.yml のイメージを差し替えるだけで済む。
# 変更前
image: postgres:16
# 変更後
image: pgvector/pgvector:pg16
同じPostgreSQL 16ベースなので、既存データはそのまま引き継がれる。
まとめ:運用の5つのルール
この構成で事故らないために押さえておくポイント。
- 外部アクセスはTailscale経由のみ:ルーターのポート開放は不要
- 開発作業はRemote-SSH:手元にランタイムを入れず、サーバー側で完結させる
- コンテナは機能単位で分ける:DB、API、フロントをそれぞれ独立に
- ポート番号はルール化:3000/5000/8000/9000番台で用途を分類
- データはvolumesで永続化:コンテナ削除=データ消滅にしない
おわりに
メインPCに全部入れても動くが、メイン環境が崩れた時の回復コストは高い。
古いPCをLinuxサーバーとして転生させておけば、
- 失敗してもサーバーだけ吹き飛ばせる
- 実験用のバックエンドを継続的に積み上げていける
- 外出先からも安全に触れる
という、地味だけど長期的に強い基盤になる。
次はこのサーバー上にFastAPIでAPIを立てたり、Grafana/Prometheusで監視を入れたり、pgvectorでベクトル検索を試したりと、拡張の余地はいくらでもある。