Relayium을 상시 실행되는 수신 서비스로 운영하기
마지막 업데이트: 2026-08-06
relayium serve --once는 들어오는 전송 하나만 처리하고 종료합니다——가끔 한 번 pull하는 용도로는 충분합니다. 하지만 어떤 기기를 상시 대기하는 수신처로 만들고 싶다면——밤사이 백업이 도착하는 홈 서버, CI가 빌드 산출물을 푸시하는 빌드 머신, 언제든 휴대폰에서 사진을 보낼 수 있는 NAS——매번 손으로 켜는 대신 serve를 계속 켜 두고 싶을 것입니다.
이 가이드는 오래 실행되는 리스너 시작하기, 누구에게 푸시를 허용할지 승인하기, 터미널 앞에 아무도 없는 경우를 위해 피어를 미리 승인해 두기, systemd 아래에서 실행하기, 그리고 sync --delete를 쓰는 보내는 쪽이 삭제를 미러링하도록 허용하는 방법을 다룹니다.
시작하기 전에
아래는 모두 relayium CLI이므로, 설치하지 않았다면 먼저 설치하세요. macOS나 Linux에서는 명령어 하나로 사전 빌드된 바이너리가 PATH에 등록됩니다:
curl -fsSL https://relayium.com/install.sh | sh
- 직접 파일을 고르고 싶거나 Windows인가요? 릴리스 페이지에서 바이너리를 받으세요 — relayium.com/cli 에 모든 설치 방법이 있습니다(Go가 있으면 go build -o relayium ./cmd/relayium 도 가능).
- relayium --version 으로 설치를 확인할 수 있습니다. 이 단계를 건너뛰면 아래 명령은 “command not found”만 출력합니다.
리스너 시작하기
1단계 전에 필요한 것
- 두 기기 모두에 CLI. relayium version은 각각 버전 문자열을 출력합니다. command not found가 나오면 그 기기에는 아직 설치되지 않은 것입니다.
- 이 기기에서 파일을 받을 디렉터리와, 거기에 쌓일 만큼의 디스크 공간.
- 보내는 쪽에서 닿을 수 있는 주소와 열린 인바운드 포트. --port를 주지 않으면 serve는 9031에서 대기합니다.
- 이 기기를 터미널 없이 돌릴 예정이라면 — 서비스로 만드는 이유가 바로 그것입니다 — 보내는 쪽의 지문을 미리 확보하세요. 아래에 따로 절이 있으며, 상시 수신기가 모든 것을 거부하는 가장 흔한 원인이 바로 이것입니다.
serve는 인증서 고정 TLS 1.3 연결을 통해 데몬 다이렉트 푸시(relayium://host:port)를 대기하고, 받은 내용을 어떤 디렉터리에 기록합니다. 시작하는 데 미리 공유해야 할 것은 없습니다——복사해 둘 핑거프린트도, 등록해 둘 서버도 없습니다:
relayium serve --dir ~/inbox
relayium serve --dir /srv/drop --port 9040 # 기본값이 아닌 포트
relayium serve --dir ~/inbox --allow-delete # sync --delete를 쓰는 보내는 쪽이 삭제를 미러링하도록 허용
파일이 떨어질 위치를 정하고 리스너를 시작합니다. 여기까지 오는 데 미리 주고받아야 할 것은 없습니다.
relayium serve --dir ~/inbox보내는 기기에서 relayium:// 주소로 이 호스트에 파일을 밀어 넣습니다.
relayium push ./report.pdf relayium://drop.example.com:9031수신기로 돌아와 승인 프롬프트에 답합니다. y를 누르면 그 지문이 authorized_fingerprints에 기록되고, 같은 기기에서 오는 다음 전송부터는 다시 묻지 않습니다.
파일이 serve를 실행한 디렉터리가 아니라 --dir에 실제로 도착했는지 확인합니다.
ls -l ~/inbox
정상적인 리스너의 모습
serve는 먼저 승인된 피어가 없다고 알리고, 새 기기의 첫 전송에서 프롬프트를 띄운 뒤 이후에는 조용합니다. 보내는 쪽은 종료 코드 0으로 끝나고 파일은 --dir 안에 있습니다.
$ relayium serve --dir ~/inbox
no authorized peers yet — you'll be asked to approve each new peer on its first push.
Incoming push from 203.0.113.7:54021
fingerprint: 74318e3b…
Accept and remember this peer? [y/N] y- --dir는 파일이 도착할 위치를 설정합니다(기본값은 현재 디렉터리).
- --port는 대기 포트를 설정합니다(기본값 9031). 보내는 쪽이 다른 곳에 있다면 방화벽에서 열어 두세요.
- --once 없이 실행하면 serve는 직접 멈추거나 프로세스 매니저가 재시작할 때까지 계속 실행되며 푸시를 계속 받아들입니다——이것이 상시 서비스가 되는 이유입니다.
누구에게 푸시를 허용할지 승인하기
새로운 피어가 처음 푸시하면, serve가 터미널에서 실행 중일 경우 그 푸시가 어디서 왔는지와 핑거프린트를 보여주고 승인할지 물어봅니다——SSH가 처음 접속하는 호스트에 대해 물어보는 것과 같은 방식입니다:
Incoming push from 203.0.113.7:54021
fingerprint: 74318e3b…
Accept and remember this peer? [y/N] y
- 한 번 y라고 답하면 그 핑거프린트가 authorized_fingerprints에 기록됩니다. 이후 같은 기기에서의 모든 푸시는 프롬프트 없이 통과합니다.
- 핑거프린트는 네트워크 주소가 아니라 기기를 식별하므로, 보내는 쪽의 IP가 바뀌어도 유지됩니다.
- 이 단계는 설계상 대화형입니다——키보드 앞에 누군가 있어야 하는데, serve가 systemd로 옮겨가면(다음 단계) 그렇지 않게 됩니다.
비대화형 환경을 위해 피어를 미리 승인하기
serve가 프롬프트를 띄울 터미널이 없을 때——systemd 서비스, 백그라운드 프로세스, 파이프——는 물어볼 수 없으므로 아직 인식하지 못한 핑거프린트를 모두 거부합니다. 대신 피어를 미리 승인해 두세요. 푸시할 기기에서 relayium id를 실행해 핑거프린트를 출력하고, 받는 쪽에서는 첫 푸시가 도착하기 전에 그것을 추가하세요:
# 푸시할 기기에서: 핑거프린트 출력하기
relayium id
# 이 상시 수신기에서: 미리 승인해 두기
relayium authorize 74318e3b...
보낼 기기에서 지문을 출력합니다. 64자리 16진수이며, 주소가 아니라 그 기기를 식별합니다.
relayium id이 수신기에서 승인합니다 — 서비스가 실제로 사용할 --config-dir와 같은 값으로. 다른 사용자로 실행하거나, 유닛은 다른 경로를 쓰는데 기본 경로로 승인하면, 지문은 서비스가 결코 읽지 않는 파일에 기록됩니다.
relayium authorize 74318e3b… --config-dir /etc/relayium
- authorize는 멱등적입니다——이미 신뢰하는 핑거프린트에 대해 다시 실행해도 아무 효과가 없습니다.
- 신원 및 신뢰 파일은 --config-dir로 지정한 디렉터리에 있으며 기본값은 ~/.config/relayium입니다(id.key/id.crt는 이 호스트의 신원, authorized_fingerprints는 피어 허용 목록).
systemd 아래에서 실행하기
재부팅과 충돌을 견디는 서비스로 만들려면 serve를 systemd에 맡기세요. --config-dir을 고정된 경로로 지정해 호스트의 신원과 피어 허용 목록이 재시작 간에도 그대로 유지되게 하세요:
# /etc/systemd/system/relayium-serve.service
[Unit]
Description=Relayium always-on receiver
After=network-online.target
[Service]
ExecStart=/usr/local/bin/relayium serve --dir /srv/drop --port 9031 --config-dir /etc/relayium --allow-delete
Restart=always
User=relayium
[Install]
WantedBy=multi-user.target
서비스를 만들기 전에, 전송을 허용할 모든 피어를 먼저 승인합니다. 서비스는 물어볼 수 없으므로 이미 신뢰되지 않은 것은 모두 거부됩니다.
위 유닛 파일을 /etc/systemd/system/relayium-serve.service에 씁니다. --config-dir는 방금 승인에 사용한 것과 같은 고정 경로를 가리켜야 합니다.
systemd를 다시 읽고 서비스를 시작하며, 재부팅 후에도 돌아오도록 활성화합니다.
sudo systemctl daemon-reloadsudo systemctl enable --now relayium-serve동작 중인지, 그리고 로그에 「전부 거부」 경고가 없는지 확인합니다. 이 구성에 고유한 것은 뒤쪽뿐입니다.
systemctl is-active relayium-servejournalctl -u relayium-serve -n 20 --no-pager
정상적인 서비스의 모습
is-active가 active라고 답하고, 승인된 피어가 없다는 시작 경고가 나타나지 않습니다. 그 경고는 어떤 발신자가 불평하기도 전에 이 서비스가 모든 전송을 거부하리라는 것을 알려 주는 유일한 한 줄입니다.
$ systemctl is-active relayium-serve
active
$ journalctl -u relayium-serve -n 20 --no-pager | grep -c 'all pushes will be rejected'
0- 이 서비스를 활성화하기 전에, 푸시를 허용해야 할 모든 피어에 대해 relayium authorize <fingerprint>를 실행해 두세요——서비스 자체는 프롬프트를 띄울 수 없습니다.
- systemctl enable --now relayium-serve로 시작하고 부팅할 때마다 다시 올라오게 하세요.
- /etc/relayium/id.key의 권한은 정확히 0600이어야 하고 소유자는 유닛의 User=여야 합니다. 그 외에는 relayium이 모두 거부합니다——0400도 포함되므로, 0600보다 더 조이는 것도 누구나 읽게 두는 것만큼이나 확실하게 서비스를 멈춥니다.
macOS에서 부팅 시 실행하기(launchd)
macOS에는 systemd가 없습니다——서비스 관리자는 launchd입니다. Mac에서 serve를 계속 실행하려면(이를테면 수신 지점으로 켜 둔 Mac mini) LaunchDaemon으로 설치해, 아무도 로그인하지 않은 부팅 시점에 시작되게 하세요. UserName을 설정해 root가 아닌 본인 계정으로 실행하고, --dir와 --config-dir에 절대 경로를 지정해 신원 및 신뢰 파일이 기존 ~/.config/relayium에 그대로 남게 하세요:
<!-- /Library/LaunchDaemons/com.relayium.serve.plist (replace YOU with your macOS username) -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key> <string>com.relayium.serve</string>
<key>UserName</key> <string>YOU</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/relayium</string>
<string>serve</string>
<string>--dir</string> <string>/Users/YOU/inbox</string>
<string>--port</string> <string>9031</string>
<string>--config-dir</string> <string>/Users/YOU/.config/relayium</string>
<string>--allow-delete</string>
</array>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>StandardOutPath</key> <string>/Users/YOU/relayium-serve.log</string>
<key>StandardErrorPath</key> <string>/Users/YOU/relayium-serve.log</string>
</dict>
</plist>
# 1) authorize each pusher first — launchd gives serve no terminal to prompt on:
relayium authorize <fingerprint> # get the fingerprint from the pusher's relayium id
# 2) save the plist above to that path, then load it (root-owned, starts at boot):
sudo chown root:wheel /Library/LaunchDaemons/com.relayium.serve.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.relayium.serve.plist
# check it's running / follow logs / stop it:
sudo launchctl print system/com.relayium.serve | grep state
tail -f ~/relayium-serve.log
sudo launchctl bootout system/com.relayium.serve
- launchd는 serve에 터미널을 주지 않으므로 프롬프트를 띄울 수 없습니다——systemd와 마찬가지로, 푸시하는 쪽마다 먼저 relayium authorize <fingerprint>를 실행해 두세요. 핑거프린트는 그 기기의 relayium id에서 얻습니다.
- KeepAlive는 serve가 충돌하면 다시 시작합니다. RunAtLoad와 /Library/LaunchDaemons 안의 LaunchDaemon을 함께 쓰면 로그인 없이 부팅 시점에 시작됩니다——화면 없이 두는 Mac mini에 딱 맞습니다.
- 로그인할 때만 도는 서비스를 원한다면, 같은 plist(UserName 키는 제거)를 ~/Library/LaunchAgents/에 두고 launchctl bootstrap gui/$(id -u) <path>로 로드하세요——부팅할 때가 아니라 로그인할 때 시작됩니다.
- macOS 응용 프로그램 방화벽이 켜져 있다면 relayium의 수신 연결을 허용하세요(시스템 설정 → 네트워크 → 방화벽). 그렇지 않으면 그 포트로 오는 푸시가 차단됩니다.
sync --delete를 쓰는 보내는 쪽이 삭제를 미러링하도록 허용하기
기본적으로 serve는 파일을 추가하거나 갱신하기만 합니다——보내는 쪽이 sync --delete를 실행해도 새 파일과 변경된 파일은 복사되지만, 요청된 삭제는 건너뛰고 받는 쪽 로그에 경고가 남습니다. 진짜 미러링을 사용하려면 --allow-delete와 함께 serve를 시작하세요. 그러면 보내는 쪽에서 삭제된 파일이 여기서도 삭제됩니다:
relayium serve --dir /srv/mirror --allow-delete
- --allow-delete는 받는 쪽에서 선택하는 옵트인입니다. 보내는 쪽은 여전히 sync --delete로 삭제를 요청해야 합니다.
- 이 옵션이 없으면 보내는 쪽이 무엇을 요청하든 이 기기에서는 아무 파일도 삭제되지 않습니다.
잘 안 될 때
다섯 중 넷은 서비스가 계속 돌면서 멀쩡해 보입니다 — 전부 거부하는 리스너도 리스너입니다. 각각 읽어서 판단할 한 줄이나 실행해서 결론 낼 명령이 있습니다.
증상, 확인, 해결
- 서비스가 시작되어 계속 떠 있는데 모든 전송이 거부됩니다.
journalctl -u relayium-serve -n 20 --no-pager # warning: no authorized peers and no terminal to approve on; all pushes will be rejected.서비스에는 터미널이 없으므로 첫 전송 승인 프롬프트를 띄울 수 없고, 보이는 것은 낯선 지문뿐입니다. 각 발신자를 미리 승인하세요. 보내는 기기에서 relayium id, 여기서 relayium authorize <지문>을 유닛과 같은 --config-dir로 실행합니다. 이 경고는 시작할 때 출력되므로 로그 첫 줄부터 남아 있습니다.
- id.key 권한이 안전하지 않다며 서비스가 아예 시작되지 않습니다.
stat -c '%a %U %n' /etc/relayium/id.key # 400 relayium /etc/relayium/id.key키 권한은 정확히 0600이어야 합니다. 0644는 물론이고, 사람들이 자주 걸리는 부분인데 0400도 안 됩니다. 더 조이는 것도 푸는 것만큼이나 확실하게 서비스를 못 뜨게 합니다. 키에 chmod 600을 적용하고 유닛의 User=가 소유자인지 확인하세요.
- 보내는 쪽이 연결이 거부되었다고 보고합니다.
relayium push ./build relayium://drop.example.com:9031 # hint: if the peer refused the connection, it may not have authorized this host.수신기가 이 발신자를 알지 못합니다. 보내는 쪽에서 relayium id를 실행하고 그 지문을 수신기에서 relayium authorize 하세요. 이미 했다면 유닛의 --config-dir 아래에서 했는지 확인합니다. 신뢰 파일은 디렉터리마다 따로이므로, ~/.config/relayium에 승인한 지문은 /etc/relayium을 읽는 서비스에게는 없는 것과 같습니다.
- 직접 serve를 실행하면 전송이 되는데 서비스로는 안 되거나 다른 기기에서는 안 됩니다.
sudo ss -tlnp | grep 9031원인은 둘이지만 확인은 하나로 갈립니다. 아무것도 대기 중이 아니라면 유닛이 활성화되지 않은 것입니다 — systemctl is-enabled relayium-serve. 대기 중이라면 포트가 막힌 것입니다. 호스트 방화벽과 클라우드 보안 그룹에서 9031을 여세요. 기본값이 아닌 --port를 쓴다면 보내는 쪽 relayium://host:N의 포트와 같아야 합니다.
- sync --delete 발신자가 요청한 삭제가 이 기기에서는 전혀 일어나지 않습니다.
journalctl -u relayium-serve | grep -i delete삭제는 수신 측이 켜야 하는 옵션이며 기본은 꺼져 있습니다. 새로 생기거나 바뀐 파일은 그대로 복사되고, 건너뛴 삭제마다 여기에 경고가 남습니다. 유닛의 ExecStart에 --allow-delete를 넣고 재시작하세요. 보내는 쪽이 요청하는 것만으로는 부족하며, 이 비대칭은 의도적입니다 — 수신기가 다른 곳에서 입력된 플래그 때문에 파일을 잃는 일은 없습니다.
자주 묻는 질문
serve는 기본적으로 어떤 포트를 대기하나요?
9031입니다. 리스너 쪽(serve --port N)과 보내는 쪽의 대상 주소(relayium://host:N) 모두에서 --port로 바꿀 수 있습니다.
모든 푸시를 일일이 손으로 승인해야 하나요?
특정 핑거프린트의 첫 푸시만 승인하면 됩니다. 그것도 serve가 터미널이 연결된 상태로 실행 중일 때만 표시됩니다. 이후에는 기억됩니다. serve를 비대화형(systemd, 파이프)으로 실행하면 프롬프트가 전혀 뜨지 않고 알 수 없는 피어는 거부됩니다——이 경우 relayium authorize로 미리 승인해 두세요.
보내는 쪽이 내 상시 수신기에 있는 파일을 삭제할 수 있나요?
serve를 --allow-delete와 함께 시작했고 보내는 쪽이 sync --delete를 실행하는 경우에만 가능합니다. --allow-delete가 없으면 삭제는 조용히 건너뛰고 나머지는 그대로 전송됩니다.
상시 수신 서비스를 운영하는 것이 무료인가요?
네. relayium serve는 무료이며 자체 호스팅 가능한 CLI의 일부입니다——연결 어느 쪽도 계정이 필요 없고 유료 등급도 없습니다.
serve는 신원과 피어 목록을 어디에 보관하나요?
기본적으로 ~/.config/relayium에 있습니다(id.key/id.crt는 이 호스트의 신원, authorized_fingerprints는 허용 목록). systemd 서비스의 경우 --config-dir을 /etc/relayium 같은 고정된 위치로 지정하세요.
macOS에서 부팅 시 serve를 실행하려면?
macOS에는 systemd가 없습니다——launchd를 사용하세요. serve를 /Library/LaunchDaemons에 LaunchDaemon으로 설치하거나(부팅 시 시작, UserName을 설정해 본인 계정으로 실행), ~/Library/LaunchAgents에 LaunchAgent로 설치하세요(로그인 시 시작). 이 가이드에는 바로 고쳐 쓸 수 있는 plist가 있습니다. Homebrew 서비스는 없습니다. launchd는 serve에 프롬프트를 띄울 터미널을 주지 않으므로, 먼저 relayium authorize로 푸시하는 쪽을 미리 승인해 두세요.
가지고 있는 어떤 기기든 무료 상시 수신기로 만들어 보세요——인증서 고정 TLS 위의 직접 푸시이며, 릴레이는 전혀 개입하지 않습니다.
CLI 받기