Skip to content

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)

If this is your first time, look over this map once before starting. You’re done after going from top to bottom once.

WhatHow longWho does it
PrepGather what you received in C:\treet (just above)5 minYou
0Create the virtual machine and install Ubuntu30–60 minYou
0-e🔴 Expand the disk — Ubuntu uses only half1 minYou
1Move the installation package into the VM5–10 minYou
2Run sudo bash install.sh1 minYou
3Readiness checksAutomaticThe installer
4Answer three questions1 minYou
5Write down the administrator password1 minYou
6Confirm it actually comes upAutomatic (a few minutes)The installer
7Turn on backups — automatic · just check1 minThe installer
7-bInstall Watchdog on this PC — restarts the CMS VM if it stops5 minYou
8Prepare remote accessWhen neededYou
9Internet address and certificate + admin entrance20 minYou (token, browser approval) + script

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:

Terminal window
# ① 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 results
Get-VMSwitch $Switch | Format-Table Name, SwitchType, NetAdapterInterfaceDescription
Get-DnsClientServerAddress -InterfaceAlias $vEth -AddressFamily IPv4 | Format-Table InterfaceAlias, ServerAddresses
Resolve-DnsName ubuntu.com -Type A | Select-Object -First 1 Name, IPAddress

Hyper-V Manager → New → Virtual Machine.

ItemValueWhy this value
GenerationGeneration 2UEFI boot. Generation 2 is the default for Ubuntu 26.04
Memory6 GB (dynamic memory off)Measured idle use is 214 MB, but installation, DB import, and video conversion need headroom
Processor4 coresVideo conversion uses the CPU — this is where extra investment is felt most
Hard disk120 GB · dynamically expandingThe program is small (about 200 MB), but media and images accumulate. Because it’s dynamic, it doesn’t take 120 GB from the start
NetworkTreeT-External (the external switch created in 0-a)The kiosks must be able to connect to this VM
Secure boot templateMicrosoft UEFI Certificate AuthorityIf 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.

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.

ExampleValue
Host PC192.168.0.4
Router (gateway)192.168.0.1
Address to give this VM192.168.0.10 (adjust for your site)

On the installation screen — Network configuration → eth0 → Edit IPv4 → Manual:

FieldValue
Subnet192.168.0.0/24
Address192.168.0.10
Gateway192.168.0.1
Name servers192.168.0.1, 8.8.8.8
Search domainsLeave empty

If you’ve already finished installing — configure it inside the VM. First check the network card name (usually eth0).

Terminal window
ip -br link
Terminal window
sudo 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]
EOF
sudo chmod 600 /etc/netplan/60-treet-static.yaml
sudo 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:

Terminal window
ip -4 addr show eth0 | grep inet
resolvectl 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.

Terminal window
ip -4 addr show | grep inet

0-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.

Terminal window
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
df -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.

Terminal window
cd ~
tar xzf treet-cms-offline-*.tar.gz
cd treet-cms-offline

Inside the folder, run the following.

Terminal window
sudo bash install.sh

It 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.

CheckIf there’s a problem
Operating systemTells 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 connectionNormal on a closed network (Docker must already be installed)
DockerIf missing, installs it automatically
ClockTells you if the time is far off
Disk · memoryStops if there isn’t enough
PortsStops if another program is already using them
File permissionsTells you if the location doesn’t enforce them (installation continues) — see below

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.

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.

🔵 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).

Terminal window
sudo bash docker/install-backup-timer.sh

Every 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.

Terminal window
sudo systemctl start treetweb-backup-site.service
ls -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).

  1. 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).

  2. 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.

  3. ⚠️ 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 IPAddresses

    If 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=add
    sudo systemctl restart hv-kvp-daemon

    When 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 happensWatchdog
The VM is turned off or pausedTurns it back on (checks every 3 minutes)
The CMS does not respond for more than 3 minutesRecords 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 respondOnly 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.

Terminal window
sudo apt install -y openssh-server
systemctl status ssh

If you can’t connect, check the following in order.

CheckCommand · location
Is SSH running?systemctl status ssh
This PC’s addressip -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
Firewallsudo 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 deviceValue
VNC serverUltraVNC (set to start automatically after installation)
Port5900 (leave the default)
PasswordNo 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.

AddressWho uses itHow it comes in
https://<site>.treetsites.com:8443Devices · large video uploads · day-to-day administrationRouter → straight to the CMS (public certificate)
https://admin-<site>.treetsites.comAdministrators only — an emergency entrance for when the router port is blockedCloudflare tunnel

On the VM:

Terminal window
sudo ./cert-check.sh # is it a public certificate · how many days until it expires
sudo ./install-tunnel.sh --check # tunnel service · response from outside

Then 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 doWhy
If you turned on the CMS VM’s firewall, open UDP 123 coming in from the internal networkIf it is blocked, devices can’t get the time
Don’t open it to the outside on the routerPublic time servers get abused for attacks
Don’t install another time-server program (chrony · ntpd) on the CMS VMThey 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).

If you’ve come this far, all of the following should work. If even one doesn’t, go back to that step.

CheckHowIf not
The admin console opensIn a browser, http://<this PC's address>:8080Step 6
You can log in as administratorWith the password you wrote down in step 5Step 5
Backups are turned onsystemctl list-timers | grep treetwebStep 7
Backup files are actually createdls -la backup/Step 7
The site address opens without warningshttps://<site>.treetsites.com:8443Step 9
The admin entrance openshttps://admin-<site>.treetsites.com9-d

Installation covers setting up the CMS. For content to appear on screens, three more things are needed.

  1. 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.
  2. Upload and schedule content — decide what plays on which screen and when.
  3. 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).

  1. Open .env in the installation folder: sudo nano .env
  2. Delete the # at the start of the #TREET_UPDATE_CHANNEL=stable line. If that line is missing, add TREET_UPDATE_CHANNEL=stable at the end.
  3. Save and bring the CMS up again: sudo docker compose up -d
TREET_UPDATE_CHANNELThis CMS is
(empty — default)Not yet in operation. It also receives development versions
stableIn operation. It receives only promoted (stable) versions — the app update list, CMS update packages and manually uploaded installers alike

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.

Terminal window
# Inside the VM, in the folder where the new package was extracted
sudo bash install.sh

Check 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.

To set up the same site again, you must have a backup file first.

Terminal window
bash docker/restore-site.sh <backup-file> # put settings and certificates back in place
sudo bash install.sh # install
bash docker/restore-site.sh <backup-file> --db-only # load the data
Terminal window
docker compose logs --tail 50 web laravel # recent logs (add laravel-scheduler media-worker for scheduled jobs)
docker compose ps # what's running
sudo bash docker/preflight.sh # re-run the readiness checks