Installing the CMS server
This is the order for setting up the CMS server at a site. It is written so you can follow it without specialist knowledge — the installer checks whether everything is ready by itself, and installs what it needs.
Before you start — gather what you received in one place
Section titled “Before you start — gather what you received in one place”Put everything you received into C:\treet on the Windows PC. If the folder doesn’t exist, create it.
The package you receive looks like this — move it as a whole folder.
C:\treet\ 읽어주세요.md ← 🔴 **Read this first.** It contains this site's values 목록.md ← list of included files and their sha256 TreeT_코드서명_공개인증서.cer ← 🔴 register on each kiosk **before installing** check-install.ps1 ← script that checks whether installation went correctly 설치파일\ ← installers for the kiosks (+ Android APK) CMS\treet-cms-offline-<version>.tar.gz ← CMS installation package 매뉴얼\ ← offline copy of this manual 키오스크셋팅\ ← kiosk PC optimization scripts(읽어주세요.md = Read me, 목록.md = file list, 설치파일 = installers, 매뉴얼 = manual, 키오스크셋팅 = kiosk setup.)
Download one more item and put it in the same place — we don’t provide it:
ubuntu-26.04.x-live-server-amd64.iso ← Ubuntu to install in the virtual machine (download from ubuntu.com)The whole process at a glance
Section titled “The whole process at a glance”If this is your first time, look over this map once before starting. You’re done after going from top to bottom once.
| What | How long | Who does it | |
|---|---|---|---|
| Prep | Gather what you received in C:\treet (just above) | 5 min | You |
| 0 | Create the virtual machine and install Ubuntu | 30–60 min | You |
| 0-e | 🔴 Expand the disk — Ubuntu uses only half | 1 min | You |
| 1 | Move the installation package into the VM | 5–10 min | You |
| 2 | Run sudo bash install.sh | 1 min | You |
| 3 | Readiness checks | Automatic | The installer |
| 4 | Answer three questions | 1 min | You |
| 5 | Write down the administrator password | 1 min | You |
| 6 | Confirm it actually comes up | Automatic (a few minutes) | The installer |
| 7 | Turn on backups — automatic · just check | 1 min | The installer |
| 7-b | Install Watchdog on this PC — restarts the CMS VM if it stops | 5 min | You |
| 8 | Prepare remote access | When needed | You |
| 9 | Internet address and certificate + admin entrance | 20 min | You (token, browser approval) + script |
0. Create the virtual machine (Hyper-V)
Section titled “0. Create the virtual machine (Hyper-V)”The CMS runs inside an Ubuntu virtual machine. On a Windows PC, create that VM first.
0-a. Turn on Hyper-V and create an external switch
Section titled “0-a. Turn on Hyper-V and create an external switch”In Control Panel → Programs → Turn Windows features on or off, check Hyper-V and reboot.
After rebooting, create the external switch before the VM. For the kiosks to connect to this VM, the VM must be connected to this PC’s physical network card.
Using the GUI: Hyper-V Manager → Virtual Switch Manager → New virtual network switch → External →
name it TreeT-External → choose the network card this PC actually uses.
⚠️ If you created it through the GUI, be sure to also do ③ Check and fill in DNS from the commands below afterwards (with a static IP, DNS gets dropped).
Using commands (administrator PowerShell) — this also takes care of missing DNS in one go:
# ① Check the network card name — the one on the same network as the kiosks (usually '이더넷' / Ethernet)Get-NetAdapter -Physical | Format-Table Name, InterfaceDescription, Status, LinkSpeed
# ② Enter the Name from the list above$Switch = "TreeT-External"$Nic = "이더넷"
# Remember the current settings before creating it — with a static IP, only DNS disappears when the switch is created$Static = (Get-NetIPInterface -InterfaceAlias $Nic -AddressFamily IPv4).Dhcp -eq "Disabled"$Dns = (Get-DnsClientServerAddress -InterfaceAlias $Nic -AddressFamily IPv4).ServerAddresses"고정 IP: $Static · 지금 DNS: $($Dns -join ', ')"
# Create the switch — skipped if it already exists (⚠️ the network drops for a few seconds)if (-not (Get-VMSwitch -Name $Switch -ErrorAction SilentlyContinue)) { New-VMSwitch -Name $Switch -NetAdapterName $Nic -AllowManagementOS $true | Out-Null}
# ③ Check and fill in DNS — if the new adapter has no DNS, fill it in# With a static IP, DNS doesn't carry over, and even with an automatic IP it sometimes isn't picked up right away.# So it is filled in **whenever it's empty**, regardless of static or automatic.$vEth = "vEthernet ($Switch)"for ($i = 0; $i -lt 10 -and -not (Get-DnsClientServerAddress -InterfaceAlias $vEth -AddressFamily IPv4).ServerAddresses; $i++) { Start-Sleep 2 }if (-not (Get-DnsClientServerAddress -InterfaceAlias $vEth -AddressFamily IPv4).ServerAddresses) { if (-not $Dns) { # If DNS was empty to begin with, use the router (gateway) and a public DNS $Gw = (Get-NetIPConfiguration -InterfaceAlias $vEth).IPv4DefaultGateway.NextHop $Dns = @($Gw, "8.8.8.8") | Where-Object { $_ } } Set-DnsClientServerAddress -InterfaceAlias $vEth -ServerAddresses $Dns Clear-DnsClientCache}
# ④ Check — both must show resultsGet-VMSwitch $Switch | Format-Table Name, SwitchType, NetAdapterInterfaceDescriptionGet-DnsClientServerAddress -InterfaceAlias $vEth -AddressFamily IPv4 | Format-Table InterfaceAlias, ServerAddressesResolve-DnsName ubuntu.com -Type A | Select-Object -First 1 Name, IPAddress0-b. Create the VM
Section titled “0-b. Create the VM”Hyper-V Manager → New → Virtual Machine.
| Item | Value | Why this value |
|---|---|---|
| Generation | Generation 2 | UEFI boot. Generation 2 is the default for Ubuntu 26.04 |
| Memory | 6 GB (dynamic memory off) | Measured idle use is 214 MB, but installation, DB import, and video conversion need headroom |
| Processor | 4 cores | Video conversion uses the CPU — this is where extra investment is felt most |
| Hard disk | 120 GB · dynamically expanding | The program is small (about 200 MB), but media and images accumulate. Because it’s dynamic, it doesn’t take 120 GB from the start |
| Network | TreeT-External (the external switch created in 0-a) | The kiosks must be able to connect to this VM |
| Secure boot template | Microsoft UEFI Certificate Authority | If left at the default (Windows), Ubuntu won’t boot |
0-c. Attach the installation image and start the VM
Section titled “0-c. Attach the installation image and start the VM”🔵 If you created the VM with the commands above, the image is already attached and the VM is running — skip to 0-d.
If you used the wizard: attach the downloaded ubuntu-26.04.x-live-server-amd64.iso to SCSI Controller → DVD
Drive in the VM settings and start the VM.
0-d. Install Ubuntu
Section titled “0-d. Install Ubuntu”You can accept the defaults, but pay attention to just two things — a static IP (+DNS) and OpenSSH.
① Network — give the VM a static IP
The kiosks connect to the CMS at this address, and during CMS installation this address is also put into the certificate. If it changes, the kiosks disconnect, so use a static IP.
First decide the address — it must be an unused address in the same range as the host PC, and outside the range the router hands out automatically (DHCP).
On the host, check the range and the router (default gateway) with ipconfig.
| Example | Value |
|---|---|
| Host PC | 192.168.0.4 |
| Router (gateway) | 192.168.0.1 |
| Address to give this VM | 192.168.0.10 (adjust for your site) |
On the installation screen — Network configuration → eth0 → Edit IPv4 → Manual:
| Field | Value |
|---|---|
| Subnet | 192.168.0.0/24 |
| Address | 192.168.0.10 |
| Gateway | 192.168.0.1 |
| Name servers | 192.168.0.1, 8.8.8.8 |
| Search domains | Leave empty |
If you’ve already finished installing — configure it inside the VM. First check the network card name (usually eth0).
ip -br linksudo tee /etc/netplan/60-treet-static.yaml > /dev/null <<'EOF'network: version: 2 ethernets: eth0: dhcp4: false addresses: [192.168.0.10/24] routes: - to: default via: 192.168.0.1 nameservers: addresses: [192.168.0.1, 8.8.8.8]EOFsudo chmod 600 /etc/netplan/60-treet-static.yamlsudo netplan try🔵 After applying, netplan try reverts to the previous settings unless you press Enter within 120 seconds — so it’s safe even if a mistake cuts off the connection.
After pressing Enter, check:
ip -4 addr show eth0 | grep inetresolvectl query ubuntu.com② Check Install OpenSSH server
Connecting from another PC to work is far easier than typing directly on the VM screen. You also use it to move the installation package.
When installation finishes, reboot, log in, and check with the command below that the static IP you chose is assigned.
ip -4 addr show | grep inet0-e. 🔴 Must do right after installation — expand the disk
Section titled “0-e. 🔴 Must do right after installation — expand the disk”The Ubuntu installer uses only half the disk. Even if you give it 120 GB, the space actually used is about 58 GB.
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lvsudo resize2fs /dev/ubuntu-vg/ubuntu-lvdf -h /It worked if df -h / shows 100 GB or more.
Once you’re here, go on to step 1 below.
1. Move the installation package into the VM
Section titled “1. Move the installation package into the VM”Move the single file CMS\treet-cms-offline-<version>.tar.gz from the package into the VM and
extract it there. Inside the VM, put it in a fixed local path. For example: /home/user/
Once it’s moved, extract it inside the VM.
cd ~tar xzf treet-cms-offline-*.tar.gzcd treet-cms-offline2. Start the installation
Section titled “2. Start the installation”Inside the folder, run the following.
sudo bash install.shIt is run with sudo because the installer installs items that aren’t ready (such as Docker) itself.
3. The installer checks things first (read and move on)
Section titled “3. The installer checks things first (read and move on)”🔵 There’s nothing for you to do here. The installer checks by itself — the following explains what it checks, and is written down so you can come back and look it up if you get stuck.
If anything is missing, it tells you on screen what to do and how.
| Check | If there’s a problem |
|---|---|
| Operating system | Tells you if it’s not Ubuntu 26.04 LTS (installation continues) |
| Name resolution (DNS) | If it fails, tells you the cause too — see below |
| Internet connection | Normal on a closed network (Docker must already be installed) |
| Docker | If missing, installs it automatically |
| Clock | Tells you if the time is far off |
| Disk · memory | Stops if there isn’t enough |
| Ports | Stops if another program is already using them |
| File permissions | Tells you if the location doesn’t enforce them (installation continues) — see below |
4. It asks three questions
Section titled “4. It asks three questions”Once the checks pass, it asks only the three questions below. Everything else is configured automatically.
① This site’s name
1) 이 사이트의 이름 (영문 소문자·숫자·하이픈). 추천: kjr7zbs ← 지금 사용 가능한 이름입니다 >(The prompt asks for the site’s name — lowercase letters, digits, and hyphens — and suggests a name that is currently available.)
You can use the suggested name as is (press Enter). This name is used to tell apart the signals exchanged with the devices.
② The address for accessing this CMS
2) 이 CMS 에 접속할 주소입니다 — 재생기가 이 주소로 붙습니다. 여러 개면 쉼표로 나열하세요. [192.168.0.5] >(The prompt asks for the address the players will use to connect to this CMS; separate several with commas.)
The value in square brackets is this PC’s address, detected automatically. If it’s correct, just press Enter.
③ The address from the outer network
3) 바깥 망에서 이 CMS 로 들어오는 주소가 있으면 적으세요 — 공유기 포워딩 주소(여러 개면 쉼표). 이름으로 붙는 것이 먼저입니다. 이름이 안 풀리는 망(폐쇄망 · 이중 NAT 바깥)에서 쓰는 IP 만 적으세요. 없으면 엔터. >(The prompt asks for any address through which devices on an outer network reach this CMS — the router forwarding address; separate several with commas. Connecting by name comes first; enter only IPs used where names do not resolve. Press Enter if there is none.)
Most sites just press Enter (leave it empty). Fill it in only when devices or a relay on an outer network — for example behind two routers — must reach this CMS by the router’s forwarding address (IP). The addresses you enter go into the CMS certificate. If it needs to change after installation, it is changed from the admin screen without reinstalling — contact your administrator.
5. Write down the administrator password
Section titled “5. Write down the administrator password”When installation finishes, the initial administrator password is shown once.
6. The installer checks itself (read and move on)
Section titled “6. The installer checks itself (read and move on)”🔵 A one-screen check appears at the end — the results of the four host settings (time zone set to the site time · Docker starting at boot · the KVP daemon on Hyper-V · the backup schedule), plus the CMS status address, the next scheduled times and this machine’s address. For a line marked ⚠️, run the command that line shows (the installation still succeeded).
Finally, the installer checks that the screens actually come up. Sometimes the screens don’t appear even though the containers are running, so it checks not “the command finished” but “it actually responds”.
설치 검증 (실제로 응답하는지 — 최초 설치는 DB 초기화로 몇 분 걸립니다)... ✓ 컨테이너 web 동작 ✓ 컨테이너 laravel 동작 ✓ 컨테이너 db 동작 ✓ 컨테이너 guacd 동작 (원격제어) ✓ 컨테이너 guacamole 동작 (원격제어) ✓ CMS 응답 확인 (HTTP 200)(Installation verification: each container — web, php, db, and the two remote-control containers guacd and guacamole — is running, and the CMS responds with HTTP 200. The first installation takes a few minutes while the DB is initialized.)
Once you see this, you can connect in a browser at http://<this PC's address>:8080.
7. Turn on backups (be sure to do this)
Section titled “7. Turn on backups (be sure to do this)”🔵 The installer does this itself — daily at 03:40 (site time) · kept 14 days · in the installation folder’s backup/. Use the steps below to check it is on or to change the location (installing it again is fine).
sudo bash docker/install-backup-timer.shEvery day before dawn, it backs up settings, certificates, and the database as one bundle and keeps them for 14 days.
Run it once by hand and check that a file is actually created.
sudo systemctl start treetweb-backup-site.servicels -la backup/7-b. Install Watchdog on this PC — it restarts the CMS if it stops
Section titled “7-b. Install Watchdog on this PC — it restarts the CMS if it stops”The CMS runs inside a virtual machine on this PC. If that VM stops, screen management at the site is cut off entirely, and the VM cannot report that by itself. So Watchdog guards it from outside (this Windows PC).
-
Install Watchdog the same way as on a player PC — see the Watchdog installation in Site deployment. When the installer asks for the CMS address, enter this VM’s address (e.g.
http://192.168.0.5:8080). -
Nothing else to do. When Watchdog starts, it finds the VM with the same IP as the CMS address by itself and starts watching it.
-
⚠️ But the Hyper-V integration tools must be running inside the VM for Watchdog to see the VM’s IP (the installer sets them up automatically — it needs internet). Check on this Windows PC (administrator PowerShell):
Terminal window Get-VMNetworkAdapter -VMName treet-cms | Select-Object -ExpandProperty IPAddressesIf the VM’s address (e.g.
192.168.0.5) does not show and the result is empty, install and start the integration tools inside the VM (needs internet):Terminal window sudo apt install -y linux-cloud-tools-$(uname -r)sudo udevadm trigger --subsystem-match=misc --action=addsudo systemctl restart hv-kvp-daemonWhen the command above shows the address, you are done. Within 10 minutes the CMS log shows “this PC’s Hyper-V VM ‘treet-cms’ was recognized as the CMS and added to monitoring”.
🔵 This state is reported for you — if the VM is running but its IP is not visible, the monitoring line in the CMS log says ”… the IP is not visible to Hyper-V: hv-kvp-daemon … must run inside the VM …”. When you see that line, run the three lines above.
🔵 If Watchdog is already installed on the PC, do not install it again — that is the case at sites where the player and the CMS share one PC. That Watchdog guards the CMS too.
With it running:
| When this happens | Watchdog |
|---|---|
| The VM is turned off or paused | Turns it back on (checks every 3 minutes) |
| The CMS does not respond for more than 3 minutes | Records it in the CMS log, and when it recovers, adds one line: “no response from … to … (N minutes)“ |
| The VM is on but only the CMS does not respond | Only reports it — Watchdog cannot touch programs inside the VM |
8. How to connect remotely (these are two different things)
Section titled “8. How to connect remotely (these are two different things)”People often confuse these, so they are written up separately.
8-a. To connect to this CMS PC — use SSH
Section titled “8-a. To connect to this CMS PC — use SSH”The installer doesn’t install it. Checking Install OpenSSH server when installing Ubuntu is what
takes care of it (see 0-d).
If you didn’t, install it now.
sudo apt install -y openssh-serversystemctl status sshIf you can’t connect, check the following in order.
| Check | Command · location |
|---|---|
| Is SSH running? | systemctl status ssh |
| This PC’s address | ip -4 addr show | grep inet |
| 🔴 Is the VM on an external switch? | Hyper-V Manager → VM settings → Network adapter. If it’s internal/private, you can’t connect from outside |
| Firewall | sudo ufw status (off by default) |
8-b. To view a device’s screen — use the remote-control feature
Section titled “8-b. To view a device’s screen — use the remote-control feature”This feature lets you look at a kiosk’s screen in the browser from the CMS admin console. It is on as soon as it is installed — there’s nothing else to do.
🔴 You must install UltraVNC on each device
Section titled “🔴 You must install UltraVNC on each device”The CMS side is ready, but the part that sends out the screen lives on the device. Install and run a VNC server (UltraVNC) on every kiosk PC.
| On the device | Value |
|---|---|
| VNC server | UltraVNC (set to start automatically after installation) |
| Port | 5900 (leave the default) |
| Password | No need to set one — each time a button is pressed, that PC’s Watchdog sets a one-time password (from CMS 2026.9.725 · Watchdog 2026.9.153) |
On the CMS side, in most cases just leave it empty (the IP reported by the device · 5900). Only when the address or port differs,
enter address[:port] in the Devices → device page → Edit basic information → “Remote access” field of the admin console — do not enter a password.
9. Internet address and certificate — set up both entrances together
Section titled “9. Internet address and certificate — set up both entrances together”A site has two entrances. The default setup uses both.
| Address | Who uses it | How it comes in |
|---|---|---|
https://<site>.treetsites.com:8443 | Devices · large video uploads · day-to-day administration | Router → straight to the CMS (public certificate) |
https://admin-<site>.treetsites.com | Administrators only — an emergency entrance for when the router port is blocked | Cloudflare tunnel |
9-e. Check
Section titled “9-e. Check”On the VM:
sudo ./cert-check.sh # is it a public certificate · how many days until it expiressudo ./install-tunnel.sh --check # tunnel service · response from outsideThen open both addresses yourself in a Windows browser — both must show a padlock icon, and no warning screen should appear. 🔵 If the first address won’t open from the network inside the site, the router is blocking connections that loop back to its own public IP (hairpinning) — check using mobile data, or use the admin entrance.
Time server — the CMS gives devices the time
Section titled “Time server — the CMS gives devices the time”Installing the CMS also starts a time server (NTP). There is no switch to turn it off — it must be ready the moment a device turns on time sync. Devices on the same internal network get the time from this CMS over UDP 123. Even on a closed network that can’t reach internet time servers, the devices stay in step with this CMS clock.
| What to do | Why |
|---|---|
| If you turned on the CMS VM’s firewall, open UDP 123 coming in from the internal network | If it is blocked, devices can’t get the time |
| Don’t open it to the outside on the router | Public time servers get abused for attacks |
| Don’t install another time-server program (chrony · ntpd) on the CMS VM | They fight over port 123 and the CMS time server can’t start — the pre-install check stops and tells you in that case. Ubuntu’s default time keeping (timesyncd) is fine |
The CMS’s own clock is kept by the VM (Hyper-V integration services time sync · Ubuntu’s default time keeping).
Checking that everything is done
Section titled “Checking that everything is done”If you’ve come this far, all of the following should work. If even one doesn’t, go back to that step.
| Check | How | If not |
|---|---|---|
| The admin console opens | In a browser, http://<this PC's address>:8080 | Step 6 |
| You can log in as administrator | With the password you wrote down in step 5 | Step 5 |
| Backups are turned on | systemctl list-timers | grep treetweb | Step 7 |
| Backup files are actually created | ls -la backup/ | Step 7 |
| The site address opens without warnings | https://<site>.treetsites.com:8443 | Step 9 |
| The admin entrance opens | https://admin-<site>.treetsites.com | 9-d |
What to do next
Section titled “What to do next”Installation covers setting up the CMS. For content to appear on screens, three more things are needed.
- Install the player (TreeTview) on the kiosk PCs, and on each PC enter this CMS’s address with F8.
🔴 Installing alone does not connect it — the player comes with the development default (
127.0.0.1). Once you enter the address, it registers itself and appears in the device list (it also gets its device certificate automatically). → Site deployment ⚠️ After entering it, look with your own eyes at the[사이트설정] 확인line in the player log. Appearing in the list is not enough — if you skip this check, you won’t be able to find the cause later. - Upload and schedule content — decide what plays on which screen and when.
- Install UltraVNC on each device — remote control is already on on the CMS side, and only the part that sends out the screen needs to be on the device (8-b).
How to use it is in the admin console documentation — device registration, scheduling, and remote control are described in screen order.
Going into operation — set the update channel to stable
Section titled “Going into operation — set the update channel to stable”A CMS that has just been installed is treated as not yet in operation (development stage). At this stage it also receives development versions (app and CMS versions still in testing). Once devices are connected, scheduling is done and all setup is finished, move it into operation — from then on it receives only versions that finished testing and were officially released (stable).
- Open
.envin the installation folder:sudo nano .env - Delete the
#at the start of the#TREET_UPDATE_CHANNEL=stableline. If that line is missing, addTREET_UPDATE_CHANNEL=stableat the end. - Save and bring the CMS up again:
sudo docker compose up -d
TREET_UPDATE_CHANNEL | This CMS is |
|---|---|
| (empty — default) | Not yet in operation. It also receives development versions |
stable | In operation. It receives only promoted (stable) versions — the app update list, CMS update packages and manually uploaded installers alike |
Updating the CMS to a new version
Section titled “Updating the CMS to a new version”It’s the same as the first installation — get the new package and run install.sh again inside it.
There is no separate update procedure.
# Inside the VM, in the folder where the new package was extractedsudo bash install.shCheck what changed with the package’s 읽어주세요.md (Read me) and the version number. After updating,
go through the checks in “Checking that everything is done” above again — especially whether the admin console opens.
When you need to reinstall (recovery)
Section titled “When you need to reinstall (recovery)”To set up the same site again, you must have a backup file first.
bash docker/restore-site.sh <backup-file> # put settings and certificates back in placesudo bash install.sh # installbash docker/restore-site.sh <backup-file> --db-only # load the dataIf something goes wrong
Section titled “If something goes wrong”docker compose logs --tail 50 web laravel # recent logs (add laravel-scheduler media-worker for scheduled jobs)docker compose ps # what's runningsudo bash docker/preflight.sh # re-run the readiness checks