발헤임, 5년 7개월만의 정식 릴리즈... 데디 띄워봐야겠지?
게임의 시즌이 돌아왔다
이젠 게임을 하기 위해 시간 맞추기도 어려워진 우리들을 위해서 또 다른 데디 서버를 준비했다
바로 발헤임(Valheim)이다
이 놈이 얼리 엑세스만 5년 7개월을 하더니 결국 이번에 정식 릴리즈가 출시됐다
나왔을 당시부터 열심히 해서 75시간 찍을만큼 열심히 했었는데 까먹고 있다가 이번에 정식 릴리즈 되면서 다시 돌아왔다

아무튼 오늘 포스팅은 발헤임 데디 서버 띄우는 것이 목적이다
최대한 간결한 과정으로 준비했고, 찾아보니 개인 편의를 위한 모드들은 클라이언트 쪽에서 설치해야하는게 많아서 서버에 별도로 모드를 적용하진 않으려고 한다
다들 바쁘다보니 귀찮게 뭘 설치하고 시작하면 다들 번거로워하니 바로 접속해서 즐길 수 있는 쪽에 좀 더 집중하고자 했다
사설이 길었다 바로 가보자
0. 서버 사양
- 24/7 돌릴 것이고 오픈월드 게임 서버니까 M1 맥미니로 골랐다
- N100은 분명 진행하다가 모드깔고 맵 넓어지면서 탐험하면 터질 것 같았고
- 인텔 맥미니는 전기세 측면에서 M1 있는데 굳이...? 싶었음
- OS: macOS Sequoia 15.7.9 (24G830) arm64
- Host: Mac mini (M1, 2020)
- Uptime: 27 days, 1 hour, 1 min
- CPU: Apple M1 (4+4) @ 3.20 GHz
- Memory: 12.02 GiB / 16.00 GiB (75%)
- Disk (/): 376.21 GiB / 926.35 GiB (41%)
1. 작업 디렉토리 생성
- M1 서버에서 터미널을 열고 다음 디렉토리를 만들자
- 참고로 아래 명령어 중 `/Users` 다음의 `mini`는 내 계정명이니 여러분은 여러분의 MacOS 계정명으로 치환해서 적어주면 된다
(앞으로 나오는 모든 명령어와 스크립트에서 경로(Path)는 여러분 환경에 맞춰서 작성해야 합니다)
mkdir -p /Users/mini/Services/valheim/{server,data,logs}
- 만들고 나면 우린 아래와 같은 디렉토리 구조를 얻게 된다
/Users/mini/Services/valheim/
├── server/ # SteamCMD가 설치하는 Valheim Dedicated Server
├── data/ # 월드 / 권한 / 백업 데이터
├── logs/ # 서버 로그
└── start.sh # 직접 관리할 실행 스크립트
- server/와 data/를 분리해둔 이유는 서버 프로그램을 재설치하거나 업데이트해도 월드 데이터를 별도로 보존하기 쉽기 때문
- 이 포스팅이 끝날 때쯤엔 아래처럼 될 것이다

2. SteamCMD 설치
- 일단 Homebrew로 SteamCMD 설치는 추천하지 않는다 (사유는 접어두겠음)
- 처음엔 아래 명령어로 설치했음
brew install --cask steamcmd
- 그랬더니 바로 에러 터짐
steamcmd.sh[xxxxx]: Starting /opt/homebrew/Caskroom/steamcmd/.../MacOS/steamcmd
- 여러가지 체크해봤는데 상황은 아래와 같았음
- Rosetta 2 설치 정상
- com.apple.quarantine 없음
- SteamCMD 바이너리는 x86_64 + arm64 Universal Binary
- arch -x86_64 강제 실행도 멈춤
- arch -arm64 강제 실행도 멈춤
- 추측하건데 Homebrew로 설치된 SteamCMD 실행 환경 문제 같았다
- 암튼 아래 명령어로 삭제헀음
brew uninstall --cask steamcmd
- 하여 Valve 공식 SteamCMD 직접 설치하도록 하자 (우선 디렉토리부터 만들어주기)
mkdir -p /Users/mini/SteamCMD
cd /Users/mini/SteamCMD
- 이젠 SteamCMD를 다운로드 받는다
curl -L \
"https://steamcdn-a.akamaihd.net/client/installer/steamcmd_osx.tar.gz" \
-o steamcmd_osx.tar.gz
- 압축 해제하고
tar -xzf steamcmd_osx.tar.gz
chmod +x steamcmd.sh
- 확인해보면 여러가지가 보인다
ls -lah /Users/mini/SteamCMD

- 아래 명령어로 테스트 가능
/Users/mini/SteamCMD/steamcmd.sh +quit
## 정상 동작하면 아래 같은 로그들이 찍힘
Checking for available updates...
Verifying installation...
Steam Console Client ...
Loading Steam API...OK
Unloading Steam API...OK
3. Valheim Dedicated Server 설치
- MacOS라서 좀 해줘야하는 것들이 있지만 생각보다 간단하니 호딱 해치우자
- 우선 SteamCMD를 실행한다
/Users/mini/SteamCMD/steamcmd.sh
- 이제 터미널 채팅이 `Steam>`으로 바뀌었을텐데 아래 명령어들을 한 줄씩 입력한다
(소제목 앞에 `3-{N}`이 달린 모든 명령어는 `Steam>` 으로 적용된 상태에서 입력하는 것이다)
3-1) 우선 설치 경로 지정
force_install_dir /Users/mini/Services/valheim/server
3-2) macOS 플랫폼임을 명시
@sSteamCmdForcePlatformType macos
## 잘 적용되면 아래같은 로그가 나옴
"@sSteamCmdForcePlatformType" = "macos"
3-3) Steam 계정 로그인
- 계정명 옵션을 `anonymous`로 주고 설치 시도하면 실패하니 바로 자신의 steam 계정명을 적어주자
- 주의할 점은 Steam에 표시되는 닉네임이 아닌, Steam Account Name (로그인할 때 쓰는 ID)를 적으면 된다
login <YOUR_STEAM_ACCOUNT_NAME>
- Steam Guard가 적용되어 있다면 `3-3 단계`에서 앱에서 로그인 승인까지 해주면 된다
3-4) 서버 설치
- 서버로 띄울 발헤임 데이터를 이제 다운로드 받아야한다 (아래 적힌 896660은 발헤임 ID)
app_update 896660 validate
- 다 받아지면 `Steam>` 터미널에서 나가면 된다
quit
3-5) 설치 확인
- 아래처럼 필요한 것들이 잘 있는지 확인해보자
ls -lah /Users/mini/Services/valheim/server

4. MacOS Steam runtime 라이브러리 연결
- 소제목이 뭔가 싶으실텐데 여기까지 하고 바로 실행시켰을 때 다음과 같은 오류가 발생했다
[S_API FAIL] SteamAPI_Init() failed; ipcserver init failed
dlopen failed trying to load:
steamclient.dylib
SteamAPI_Init(): Could not determine Steam client install directory.
Steamworks.NET GameServer.Init() failed.
Steam is not initialized
Awake of network backend failed
- 결국 `steamclient.dylib`란 파일이 없단 소린데 사실 실제로 `ls` 명령어로 확인해보면 해당 파일이 존재한다
- 따라서 있는데 못 읽는단 소리
- 그래서 우린 필요한 dylib symlink 생성해줄 것이다
- 쉽게 말해 컴퓨터가 읽을 수 있는 경로에 해당 파일이 있는 척, `경로(Path)를 저장한 특수 파일을 만들어줄 것`이다
- 아래 명령어로 우선 이동
cd /Users/mini/Services/valheim/server/valheim_server
- 그리고 필요한 파일의 심볼릭 링크(symlink)를 생성해주자
ln -sf /Users/mini/SteamCMD/steamclient.dylib steamclient.dylib
ln -sf /Users/mini/SteamCMD/libtier0_s.dylib libtier0_s.dylib
ln -sf /Users/mini/SteamCMD/libvstdlib_s.dylib libvstdlib_s.dylib
- 아래 명령어로 조회하면 저렇게 `->` 표시로 실제 파일들의 경로를 보여준다
ls -l \
steamclient.dylib \
libtier0_s.dylib \
libvstdlib_s.dylib

5. 서버 실행 간이 스크립트 작성
- 이 부분에서는 간단한 스크립트를 작성해서 서버가 띄워지는지 체크하고 넘어갈 것이다
- 이후에 자동화와 알림을 만들고 나서 테스트하면 어디서 꼬이는지 찾는게 귀찮아지기 때문임
- 우선 서버를 실행시킬 스크립트를 작성하자 (vim이나 nano 편한 쪽으로 편집하자)
# nano.ver
nano /Users/mini/Services/valheim/start.sh
# vim.ver
vim /Users/mini/Services/valheim/start.sh
- 아래 내용을 수정해서 붙여넣어준다 (월드 이름/비밀번호 등등을 수정하면 된다)
#!/bin/zsh
BASE="/Users/mini/Services/valheim"
SERVER="$BASE/server"
DATA="$BASE/data"
LOGS="$BASE/logs"
mkdir -p "$DATA" "$LOGS"
cd "$SERVER" || exit 1
# macOS에서 Valheim Dedicated Server가 Steam runtime dylib를 찾도록 지정
export DYLD_LIBRARY_PATH="/Users/mini/SteamCMD:/Users/mini/Services/valheim/server/valheim_server"
export DYLD_FRAMEWORK_PATH="/Users/mini/SteamCMD/Frameworks"
exec "$SERVER/valheim_server/Valheim" \
-name "<<YOUR SERVER NAME>>" \
-port 2456 \
-world "<<YOUR_WORLD_NAME>>" \
-password "<<YOUR_SERVER_PASSWORD>>" \
-savedir "$DATA" \
-public 1 \
-crossplay \
-logFile "$LOGS/server.log" \
-saveinterval 900 \
-backups 15 \
-backupshort 3600 \
-backuplong 43200
- `<<~>>`로 작성된 부분을 치환하고 실행 권한을 부여한다
chmod 700 /Users/mini/Services/valheim/start.sh
- 우리는 crossplay 옵션을 줄 것이다
- crossplay는 간단히 말하면 서로 다른 플랫폼 유저끼리 같이 접속할 수 있게 해주는 기능이다
(왜 crossplay를 썼는지는 아래 접은 글 참조)
crossplay를 쓴 이유
- 사실 내 친구들은 steam판으로만 접속할 것이라 꼭 crossplay를 안써도 됐음
- 또한 crossplay를 안써야 여러 모드를 설치할 수 있지만, 이번엔 '최대한 간단하게 구축 및 접속'할 수 있을 것에 초점을 뒀기 때문에 생략하였다
- crossplay를 쓰면 PlayFab 기반 접속/Join Code를 쓸 수 있게 되기 때문에 편의성 증가 (*)
- crossplay 옵션을 빼면 2456~2457 UDP 포트를 포트포워딩을 해줘야한다 (이것도 내겐 좀 귀찮은 부분이었음)
- 아무튼 최대한 쉽고 빠르게 구축할 수 있고, 당장은 모드가 필요 없고, 발헤임의 많은 모드가 클라이언트에서 설치가 필요하기에 직접 구축하고 포트포워딩하는 부분을 건너 뛰기위해 사용하였다
- 돈스타브는 죄다 서버에 적용했어야 했는데 이와 달랐던 점이 신기했음
- 그래서 찾아보니 발헤임은 캐릭터의 많은 데이터를 유저(클라이언트) 측에 저장하고, 그렇기 때문에 서버에서 모드를 적용해봐야 유저 측의 데이터를 쉽게 변경할 수 없는 구조였다
- 쉽게 말해 [플레이어 1]의 무게 소지량을 300에서 400으로 늘려주고 싶은데, 그 데이터는 사실상 유저 측의 캐릭터에 저장되어있는 값이라서 유저가 모드를 써야 적용되는 경우가 많다는 것이다
- 이는 많은 모더들의 고민인 것도 같았음 (내 서버에 들어오는 유저가 치터(cheater)인지 구분하기가 어려워지는 셈)
(*) PlayFab 기반 접속 : 발헤임 서버와 친구들 사이를 중계해주는 온라인 연결 서비스
(*) Join Code : 그 서버를 쉽게 찾기 위한 임시 접속 코드
- 공식 문서에서 -crossplay를 넣으면 PlayFab을 쓰고, 빼면 Steam backend를 쓴다고 설명하고 있음
출처 : https://www.valheim.com/support/a-guide-to-dedicated-servers/?utm_source=chatgpt.com
Valheim
www.valheim.com
- 그 외 옵션 설명은 아래 간단하게
crossplay
- PlayFab 기반 Crossplay backend를 사용함
- 공유기 포트포워딩 불필요
- M1의 UDP 2456-2457을 인터넷에 직접 공개할 필요 없음
- Steam 외 플랫폼과도 접속 가능
- Join Code 사용 가능
- 이 옵션이 없으면 Steam backend가 사용됨
public 1
- 서버를 서버 브라우저에 공개함
- Join Code 기반 Crossplay 서버에서는 -public 1을 사용하는 것이 가장 단순
- 공식 문서상 -public 0은 서버 브라우저에서 숨기며 Join IP 방식으로만 접속하는 설정임
- 서버 비밀번호는 별도로 유지
savedir
- 월드 / 권한 / 백업 데이터를 다음 경로에 저장
/Users/mini/Services/valheim/data
- 서버 바이너리와 데이터를 분리하기 위한 설정임
자동 저장 / 백업
-saveinterval 900 # 현재 진행을 15분마다 저장
-backups 15 # 최근 복원 지점을 약 1시간 단위로 확보
-backupshort 3600 # 오래된 복원 지점을 12시간 단위로 확보
-backuplong 43200 # 복원 지점을 총 15개 유지
6. 서버 실행 간이 스크립트 실행 및 로그 확인
- 아래 명령어로 실행한다
/Users/mini/Services/valheim/start.sh
- 첫 실행에는 월드 생성하는 단계가 있어서 조금 걸릴 수 있음
- 적당히 다 실행된 것 같으면 터미널에서 아래 명령어를 통해 로그를 확인하자
tail -f /Users/mini/Services/valheim/logs/server.log
- 로그를 보면 세션이 잘 떴는지, Join Code가 무엇인지 알려준다

- 별도로 서버에서 Join Code를 확인해야할 때는 아래 명령어를 쓴다
grep -E "registered with join code|active with" \
/Users/mini/Services/valheim/logs/server.log | tail -5
7. 서버 실행 스크립트 완성하기
- 6번까지 했다면 게임 직접 켜서 서버에 접속 테스트 해보는 것을 추천한다
- 다만 내용이 겹치니 테스트 하실 분들은 Join Code를 적어두고, `10. 발헤임 데디 서버 접속해보기`를 보시고 테스트 해보시면 되겠다
- 일단 스크립트를 완성할 것인데 추가 되는 내용은 Discord에 서버에 Join Code를 보내는 스크립트를 추가할 것이다
- 그렇지 않으면 매번 내가 서버에 접속해서 친구들에게 공유해줘야하기 때문이다
- 귀찮으니 서버를 띄울 때마다 Join Code를 보내도록 설정할 것이다
7-1) Discord 웹훅 발행 및 서버에 저장하기
- Discord 웹훅 발행은 아래 포스팅 참조 (https://ratatou2.tistory.com/392)
- 발행하고 나면 아래 포스팅에 있는 curl 테스트까지 꼭 해보도록 하자 (아마 문제 없이 될 것임)
Discord 웹훅(Webhook) 만드는 방법 (feat. 디코 알림봇 3초만에 만들기)
하도 자주 써서 다른 포스팅에 인용할 글을 따로 작성함- 우선 웹훅을 걸어줄 채널을 하나 파고 오른쪽 끝의 톱니를 눌러준다- 연동을 누르고 `웹후크 만들기`를 누른다- 필요한 이름과 이미지를
ratatou2.tistory.com
- 이제 발행한 Discord 웹훅을 서버에 저장해주기 위해 아래 명령어 입력
mkdir -p /Users/mini/.config/valheim
nano /Users/mini/.config/valheim/discord-webhook
- 파일에는 아까 복사한 웹훅 한줄만 적어넣고 저장한다
https://discord.com/api/webhooks/XXXXXXXX/XXXXXXXX
- 웹훅이니까 권한을 제한한다 (아무나 읽고 쓰지 못하도록)
chmod 600 /Users/mini/.config/valheim/discord-webhook
7-2) 기존 스크립트 백업 및 알림 오도록 수정하기
- 기존 스크립트를 백업 떠두고
cp /Users/mini/Services/valheim/start.sh /Users/mini/Services/valheim/start.sh.bak
- 새로운 내용으로 덮어 씌울 것이다
nano /Users/mini/Services/valheim/start.sh
- 스크립트가 너무 길어서 접은 글에 넣음
#!/bin/zsh
# ============================================================
# Valheim Dedicated Server
# M1 macOS + Crossplay + Discord Join Code Notification
# ============================================================
BASE="/Users/mini/Services/valheim"
SERVER="$BASE/server"
DATA="$BASE/data"
LOGS="$BASE/logs"
LOG_FILE="$LOGS/server.log"
WEBHOOK_FILE="/Users/mini/.config/valheim/discord-webhook"
# ============================================================
# 기본 디렉터리 준비
# ============================================================
mkdir -p "$DATA" "$LOGS"
cd "$SERVER" || exit 1
# ============================================================
# Steam runtime
#
# M1 macOS에서 steamclient.dylib 등을 찾기 위해 필요
# ============================================================
export DYLD_LIBRARY_PATH="/Users/mini/SteamCMD:/Users/mini/Services/valheim/server/valheim_server"
export DYLD_FRAMEWORK_PATH="/Users/mini/SteamCMD/Frameworks"
# ============================================================
# 이전 서버 로그 보관
# ============================================================
if [[ -s "$LOG_FILE" ]]; then
OLD_LOG="$LOGS/server-$(date '+%Y%m%d-%H%M%S').log"
mv "$LOG_FILE" "$OLD_LOG"
echo "Previous log archived:"
echo " $OLD_LOG"
fi
touch "$LOG_FILE"
# ============================================================
# Discord Join Code 감시 함수
# ============================================================
notify_join_code() {
if [[ ! -f "$WEBHOOK_FILE" ]]; then
echo "Discord webhook file not found:"
echo " $WEBHOOK_FILE"
return 1
fi
local WEBHOOK_URL
WEBHOOK_URL="$(tr -d '\r\n' < "$WEBHOOK_FILE")"
if [[ -z "$WEBHOOK_URL" ]]; then
echo "Discord webhook URL is empty."
return 1
fi
local LAST_CODE=""
echo "Waiting for Valheim Join Code..."
while true; do
local JOIN_CODE
JOIN_CODE=$(
tail -n 1000 "$LOG_FILE" 2>/dev/null \
| sed -nE 's/.*registered with join code ([0-9]+).*/\1/p' \
| tail -1
)
# 숫자로 된 Join Code가 발견되고,
# 이전에 전송한 코드와 다른 경우에만 Discord 전송
if [[ "$JOIN_CODE" =~ ^[0-9]+$ ]] \
&& [[ "$JOIN_CODE" != "$LAST_CODE" ]]; then
local PAYLOAD
PAYLOAD=$(
printf \
'{"content":"🟢 **Valheim 서버 온라인**\\n서버: **<<서버명>>**\\nJoin Code: **%s**"}' \
"$JOIN_CODE"
)
if curl -fsS \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"$WEBHOOK_URL" \
>/dev/null
then
echo "Discord Join Code sent: $JOIN_CODE"
LAST_CODE="$JOIN_CODE"
else
echo "Discord notification failed."
fi
fi
sleep 2
done
}
# ============================================================
# Valheim 실행
# ============================================================
"$SERVER/valheim_server/Valheim" \
-name "<<서버명>>" \
-port 2456 \
-world "<<월드명>>" \
-password "<<비밀번호>>" \
-savedir "$DATA" \
-public 1 \
-crossplay \
-modifier resources more \
-modifier deathpenalty easy \
-logFile "$LOG_FILE" \
-saveinterval 900 \
-backups 15 \
-backupshort 3600 \
-backuplong 43200 &
VALHEIM_PID=$!
echo "Valheim started."
echo "PID: $VALHEIM_PID"
# ============================================================
# Discord Join Code 감시 시작
# ============================================================
notify_join_code &
NOTIFIER_PID=$!
echo "Join Code notifier started."
echo "PID: $NOTIFIER_PID"
# ============================================================
# 서버 정상 종료 처리
#
# 추후 launchd에서 SIGTERM을 받더라도
# Valheim에는 SIGINT(Ctrl+C)를 전달
# ============================================================
shutdown_server() {
echo
echo "Stopping Valheim server gracefully..."
# Join Code 감시기 종료
if kill -0 "$NOTIFIER_PID" 2>/dev/null; then
kill "$NOTIFIER_PID" 2>/dev/null
fi
# Valheim 정상 종료
if kill -0 "$VALHEIM_PID" 2>/dev/null; then
echo "Sending SIGINT to Valheim..."
kill -INT "$VALHEIM_PID"
wait "$VALHEIM_PID"
fi
echo "Valheim server stopped."
exit 0
}
trap shutdown_server INT TERM
# ============================================================
# Valheim 프로세스 종료까지 대기
# ============================================================
wait "$VALHEIM_PID"
VALHEIM_STATUS=$?
# Valheim이 자체 종료된 경우 notifier도 정리
kill "$NOTIFIER_PID" 2>/dev/null || true
wait "$NOTIFIER_PID" 2>/dev/null || true
echo "Valheim exited with status: $VALHEIM_STATUS"
exit "$VALHEIM_STATUS"
- 중간에 보면 간이 스크립트 때와는 달리 두가지 옵션이 추가되어있다
- 아래 두가지가 그것인데 초보자 친구들을 위해 난이도 조절을 하느라 추가하였음
-modifier resources more ## 자원량 증가
-modifier deathpenalty easy ## 죽을 시 패널티 수준
Resources 단계
| muchless | ×0.5 |
| less | ×0.75 |
| 기본값 | ×1 |
| more | ×1.5 |
| muchmore | ×2 |
| most | ×3 |
DeathPenalty 단계
| 설정 | 착용 장비 | 나머지 (인벤토리) | 스킬 감소 |
| casual | 유지 | 무덤에 드롭 | 1% |
| veryeasy | 드롭 | 드롭 | 1% |
| easy | 드롭 | 드롭 | 2.5% |
| 기본 | 드롭 | 드롭 | 5% |
| hard | 드롭 | 파괴 | 7.5% |
| hardcore | 파괴 | 파괴 | 사실상 100% |
- 이제 이 스크립트도 실행 권한을 주면 된다
chmod +x /Users/mini/Services/valheim/start.sh
8. launchd 서비스로 등록
- 여기서부터는 사실 선택이다 (꼭 안해도 되지만 하면 관리 편리성이 올라가는)
- 이제 서버 관리하기 쉽게 서비스로서 등록하고 명령어도 단축어로 등록해줄 것이다
- 이것까지 하고 나면 아래 명령어로 간단하게 발헤임 서버를 관리할 수 있다
valheimctl start # 서버 시작
valheimctl stop # 서버 정지
valheimctl restart # 서버 재실행
valheimctl status # 서버 상태 확인
valheimctl log # 서버 로그 확인
valheimctl code # Join Code 확인
8-1) 로그 파일 준비
- 아까 초반에 디렉토리를 만들긴 했지만 확실히 하고자 아래 명령어로 디렉토리를 만들어준다
mkdir -p /Users/mini/Services/valheim/logs
- 이제 아래처럼 로그가 분리된 것임
server.log
→ Valheim 자체 게임 서버 로그
launchd.out.log
→ start.sh 출력
launchd.err.log
→ launchd/start.sh 오류
8-2) LaunchDaemon plist 생성
- 아래 명령어로 파일을 열고
sudo nano /Library/LaunchDaemons/com.valheim.dedicated.plist
- 아래 내용 그대로 붙여넣는다
<?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.valheim.dedicated</string>
<!-- ================================================== -->
<!-- 실행 명령 -->
<!-- ================================================== -->
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/Users/mini/Services/valheim/start.sh</string>
</array>
<!-- ================================================== -->
<!-- mini 사용자 권한으로 실행 -->
<!-- ================================================== -->
<key>UserName</key>
<string>mini</string>
<!-- ================================================== -->
<!-- 작업 디렉터리 -->
<!-- ================================================== -->
<key>WorkingDirectory</key>
<string>/Users/mini/Services/valheim</string>
<!-- ================================================== -->
<!-- 환경 변수 -->
<!-- ================================================== -->
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/mini</string>
<key>USER</key>
<string>mini</string>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<!-- ================================================== -->
<!-- Mac 부팅 시 실행 -->
<!-- ================================================== -->
<key>RunAtLoad</key>
<true/>
<!-- ================================================== -->
<!-- 비정상 종료 시 자동 재시작 -->
<!-- 정상 종료(exit 0) 시에는 재시작하지 않음 -->
<!-- ================================================== -->
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<!-- ================================================== -->
<!-- 종료 시 정상 저장할 시간을 충분히 줌 -->
<!-- ================================================== -->
<key>ExitTimeOut</key>
<integer>60</integer>
<!-- ================================================== -->
<!-- launchd 로그 -->
<!-- ================================================== -->
<key>StandardOutPath</key>
<string>/Users/mini/Services/valheim/logs/launchd.out.log</string>
<key>StandardErrorPath</key>
<string>/Users/mini/Services/valheim/logs/launchd.err.log</string>
</dict>
</plist>
8-3) plist 문법 검사
- 혹시 모르니 아래 명령어로 문법 검사를 한다
sudo plutil -lint /Library/LaunchDaemons/com.valheim.dedicated.plist
- 정상이면 아래처럼 나온다
/Library/LaunchDaemons/com.valheim.dedicated.plist: OK
8-4) plist 권한 설정
# LaunchDaemon plist는 root 소유로 변환
sudo chown root:wheel /Library/LaunchDaemons/com.valheim.dedicated.plist
# 권한 변경
sudo chmod 644 /Library/LaunchDaemons/com.valheim.dedicated.plist
# 아래 명령어 입력 후
ls -l /Library/LaunchDaemons/com.valheim.dedicated.plist
# 아래 예시처럼 나오는지 확인 (`-rw-r--r--`로 나오면 된다)
-rw-r--r-- 1 root wheel ... com.valheim.dedicated.plist
8-5) launchd에 등록
# 예전에 같은 이름으로 등록되어있으면 꼬이니까 이것 먼저
sudo launchctl bootout system/com.valheim.dedicated 2>/dev/null || true
# 그 다음에 적용
sudo launchctl bootstrap system \
/Library/LaunchDaemons/com.valheim.dedicated.plist
- `RunAtLoad=true`라서 등록하면 바로 서버가 실행된다
- 이제 서버 로그 확인하고 부팅될 때까지 좀 기다려서 Join Code가 디스코드 웹훅 알림으로 오는지 체크하면 된다
9. valheimctl 만들기
- 8번을 했다면 필수과정인 부분
- 귀찮고 긴 launchctl 명령을 외울 필요 없게 하기 위해 필수 과정이다
- 우선 아래 순서대로 디렉토리를 만들고, 파일을 만들고, 내용을 붙여넣는다
# 디렉토리 생성
sudo mkdir -p /usr/local/bin
# 파일 생성
sudo nano /usr/local/bin/valheimctl
- 복붙할 스크립트가 넘 기니까 접은글로..
#!/bin/zsh
# ============================================================
# Valheim Dedicated Server Control
# ============================================================
LABEL="com.valheim.dedicated"
SERVICE="system/$LABEL"
PLIST="/Library/LaunchDaemons/$LABEL.plist"
BASE="/Users/mini/Services/valheim"
LOGS="$BASE/logs"
SERVER_LOG="$LOGS/server.log"
LAUNCHD_OUT="$LOGS/launchd.out.log"
LAUNCHD_ERR="$LOGS/launchd.err.log"
VALHEIM_PATTERN="$BASE/server/valheim_server/Valheim"
# ============================================================
# 상태 확인 함수
# ============================================================
is_loaded() {
launchctl print "$SERVICE" >/dev/null 2>&1
}
is_running() {
pgrep -f "$VALHEIM_PATTERN" >/dev/null 2>&1
}
get_pid() {
pgrep -f "$VALHEIM_PATTERN" | head -1
}
get_join_code() {
if [[ ! -f "$SERVER_LOG" ]]; then
return 1
fi
tail -n 2000 "$SERVER_LOG" 2>/dev/null \
| sed -nE 's/.*registered with join code ([0-9]+).*/\1/p' \
| tail -1
}
# ============================================================
# START
# ============================================================
start_server() {
if ! is_loaded; then
echo "LaunchDaemon is not loaded."
echo "Loading..."
sudo launchctl bootstrap system "$PLIST"
if [[ $? -ne 0 ]]; then
echo "Failed to load LaunchDaemon."
exit 1
fi
echo "LaunchDaemon loaded."
elif is_running; then
echo "Valheim server is already running."
echo "PID: $(get_pid)"
exit 0
else
echo "Starting Valheim server..."
sudo launchctl kickstart "$SERVICE"
fi
sleep 2
if is_running; then
echo "Valheim server started."
echo "PID: $(get_pid)"
else
echo "Start requested."
echo "Check:"
echo " valheimctl log"
echo " valheimctl launchlog"
fi
}
# ============================================================
# STOP
# ============================================================
stop_server() {
if ! is_loaded; then
echo "LaunchDaemon is not loaded."
exit 0
fi
if ! is_running; then
echo "Valheim server is already stopped."
exit 0
fi
echo "Stopping Valheim server gracefully..."
echo "PID: $(get_pid)"
# start.sh가 SIGTERM을 받으면
# Valheim 프로세스에 SIGINT를 전달하여 정상 저장/종료
sudo launchctl kill SIGTERM "$SERVICE"
echo -n "Waiting"
for i in {1..60}; do
if ! is_running; then
echo
echo "Valheim server stopped."
return 0
fi
echo -n "."
sleep 1
done
echo
echo "Server did not stop within 60 seconds."
echo "SIGKILL was NOT sent to protect world data."
exit 1
}
# ============================================================
# RESTART
# ============================================================
restart_server() {
echo "Restarting Valheim server..."
if is_running; then
stop_server || exit 1
fi
echo
start_server
}
# ============================================================
# STATUS
# ============================================================
status_server() {
echo "=== Valheim Dedicated Server ==="
echo
if is_loaded; then
echo "LaunchDaemon : loaded"
else
echo "LaunchDaemon : not loaded"
fi
if is_running; then
PID="$(get_pid)"
echo "Server : RUNNING"
echo "PID : $PID"
else
echo "Server : STOPPED"
fi
JOIN_CODE="$(get_join_code)"
if [[ -n "$JOIN_CODE" ]]; then
echo "Join Code : $JOIN_CODE"
else
echo "Join Code : not available"
fi
echo
}
# ============================================================
# LOG
# ============================================================
show_log() {
if [[ ! -f "$SERVER_LOG" ]]; then
echo "Server log not found:"
echo " $SERVER_LOG"
exit 1
fi
tail -f "$SERVER_LOG"
}
# ============================================================
# LAUNCHD LOG
# ============================================================
show_launchd_log() {
echo "=== launchd stdout ==="
tail -n 30 "$LAUNCHD_OUT" 2>/dev/null
echo
echo "=== launchd stderr ==="
tail -n 30 "$LAUNCHD_ERR" 2>/dev/null
}
# ============================================================
# JOIN CODE
# ============================================================
show_code() {
JOIN_CODE="$(get_join_code)"
if [[ -z "$JOIN_CODE" ]]; then
echo "Join Code not found."
exit 1
fi
echo "$JOIN_CODE"
}
# ============================================================
# USAGE
# ============================================================
usage() {
echo "Usage:"
echo
echo " valheimctl start Start server"
echo " valheimctl stop Gracefully stop server"
echo " valheimctl restart Gracefully restart server"
echo " valheimctl status Show status"
echo " valheimctl log Follow Valheim server log"
echo " valheimctl launchlog Show launchd logs"
echo " valheimctl code Show current Join Code"
echo
}
# ============================================================
# COMMAND
# ============================================================
case "$1" in
start)
start_server
;;
stop)
stop_server
;;
restart)
restart_server
;;
status)
status_server
;;
log)
show_log
;;
launchlog)
show_launchd_log
;;
code)
show_code
;;
*)
usage
exit 1
;;
esac
- 실행 권한을 주도록 하자
sudo chmod +x /usr/local/bin/valheimctl
- 아래 명령어를 입력해서 예시처럼 나오면 된 것이다
# 명령어
which valheimctl
# 출력 예시
/usr/local/bin/valheimctl

- 이제 간단한 명령어를 통해 서버 상태를 조절 및 확인할 수 있다

- 옵션에 넣어뒀기 때문에 비정상 종료 시 자동 복구도 된다
- 나중에 서버 자원 옵션 수정하면 스크립트 수정하고 서버 재실행하면 된다 (`valheimctl restart`)
10. 발헤임 데디 서버 접속해보기
- 분명 짧게 쓸려고 했는데 드릅게 길어졌다;;
- 죄송(?!)하다는 말씀드리며 드디어 마지막으로 게임 서버에 접속해보고자 한다 (근데 제 탓은 아닌.. ㅠ)
- 우선 Steam을 켜고 발헤임(Valheim)을 켠다
- 게임 시작 클릭

- (원하는 캐릭터를 고르면) 서버를 고를 수 있는 창이 뜬다
- 여기서 서버 추가를 누르고, 디스코드로 받은 Join Code를 입력한다

- 그럼 비밀번호 입력 창이 뜨는데, 여기에는 스크립트에 적었던 서버 비밀번호를 적는다

- 그러고 나면 접속 됨!


- 아직 직접 플레이는 안해봐서 어떻다까지는 모르겠지만 한번 또 플레이 해보고 수정할 부분이 있으면 남기도록 하겠다
