# Install FsRdpServer on Linux

A complete guide from a freshly installed Linux server to your first working client
connection, for Ubuntu Server, Debian and Raspberry Pi OS (64-bit). Every command is
explained individually.

> Plain-text version of <https://www.fssoft.de/FsRdp/en/InstallingServerLinux.aspx>
> for AI assistants and printing. Its content matches the HTML version;
> the web page is authoritative in case of doubt.
>
> Automatically generated from the page and language resources by
> FsRdp/Build-InstallDocs.ps1. Edit those sources rather than this file.

> **Try It without a License:** You do not need a license to install the server. Without a
> license file, a server runs **as a trial installation for 30 days** with full
> functionality; see section 7 for details. We agree on the server name and license once
> it is running in your environment.

## On This Page

1. What Should Be Running at the End
2. Prepare the Fresh Linux Server
3. Server Name: DNS with A and AAAA Records
4. Router, Port Access and Firewall
5. The TLS Certificate (PFX File) in Detail
6. Install the Server Package
7. Trial Period or License (Generate the Installation ID)
8. Install the Certificate, License and Password
9. Validate and Start the Service
10. Browser Viewer on Port 8890 with Its Own Certificate
11. Your First Connection Test
12. Ongoing Operation: Backups, Updates and Renewal
13. Common Problems

---

## 1. What Should Be Running at the End

FsRdpServer is a **relay server**. It does not capture screens or store session content;
it connects the participants in a room. All clients connect to the server, never
directly to one another. Only the server needs to be reachable, rather than any client.

After installation, the service listens on these TCP ports:

| Also allow port | Purpose | Required? |
|---|---|---|
| `8888` | Main connection: control, screen content, files and remote control | yes |
| `8889` | Voice transmission (separate channel) | yes |
| `8890` | Browser viewer for viewers without an installed client | optional, requires a license (available during the trial) |
| `80` | Only for issuing and renewing Let's Encrypt certificates | optional |

The Linux package is **self-contained** ; no system-wide .NET runtime installation is
required. Use:

- `linux-x64` for Ubuntu Server, Debian and similar 64-bit Intel/AMD systems;
- `linux-arm64` for Raspberry Pi OS (64-bit) and other ARM64 systems.

---

## 2. Prepare the Fresh Linux Server

Enter all following commands in a terminal, either directly on the device or via SSH.
`sudo` means “run with administrative permissions”; Linux asks for your password the
first time.

### Step 1: Update the System

```bash
sudo apt update
sudo apt full-upgrade -y
sudo reboot
```

Sign in again after restarting. A server that will be reachable from the internet should
be up to date from the outset.

### Step 2: Install Required Utilities

```bash
sudo apt install -y unzip openssl
```

`unzip` extracts the server package; `openssl` is needed later to inspect and create the
PFX file.

### Step 3: Check Time and Time Zone

Certificates and licenses have validity periods. If the server clock is several minutes
wrong, connections may fail with seemingly inexplicable errors.

```bash
timedatectl
sudo timedatectl set-timezone Europe/Berlin
```

The output of `timedatectl` should show `System clock synchronized: yes` .

### Step 4: Assign a Fixed Local IP Address

The router must forward ports to a stable address. The simplest solution is a fixed DHCP
assignment in the router (“always assign the same IPv4 address to this device”), so no
Linux configuration change is required. View current addresses with:

```bash
ip -brief address show
```

### Step 5: IPv6: Keep the Last Part of the Address Fixed

An IPv6 address has two parts: the *prefix*assigned by your provider and the *interface
ID* chosen by the device. Many systems choose a new interface ID whenever the prefix
changes, which breaks DNS records and router access rules. With NetworkManager, the
default on Raspberry Pi OS and current Ubuntu desktops, fix the interface ID as follows:

```bash
nmcli connection show
sudo nmcli connection modify "Wired connection 1" \
  ipv6.addr-gen-mode eui64 ipv6.token ::159
sudo nmcli connection up "Wired connection 1"
```

The connection name comes from the first line; `::159` can be chosen freely. The
server's IPv6 address will then always end in `::159`regardless of the prefix. On
servers using `systemd-networkd` or `netplan` , achieve the same result with the
parameter `Token=` or a statically configured address.

---

## 3. Server Name: DNS with A and AAAA Records

### 3.1 Why a Name Rather than an IP Address?

Clients connect **using a DNS name**, for example `server.ihrefirma.de`, never an IP
address. There are two essential reasons: the TLS certificate is issued for a name, and
your license is tied to that exact same name.

> **Remember:** Licensed host name = certificate name = name entered in the client. All
> three must match character for character.

### 3.2 What Are A and AAAA Records?

A DNS record translates a name into an address. There are two types because the internet
uses two address types:

| Record | Points To | Example |
|---|---|---|
| `A` | an IPv4 address | `203.0.113.45` |
| `AAAA` | an IPv6 address | `2a02:810d:7087:8800::159` |

Pronounce `AAAA` as “quad A”. Both records may exist for the same name. Important: the
AAAA record points to the IPv6 address **of the server itself**, rather than the router;
every IPv6 device has its own globally valid address.

### 3.3 Find Your Public Addresses

Run on the server:

```bash
# Öffentliche IPv4-Adresse des Anschlusses
curl -4 https://api.ipify.org; echo

# Öffentliche IPv6-Adresse dieses Servers
curl -6 https://api6.ipify.org; echo
```

If the first command returns an answer, you have public IPv4; if the second does, you
have IPv6.

### 3.4 DS-Lite: No Public IPv4 Address

Many cable and fiber connections in Germany use **DS-Lite**. Your connection shares a
public IPv4 address with many other customers rather than having its own. The
consequences:

- **IPv4 port forwarding has no effect.** You can configure it, but it will never be
  reached.
- **Only IPv6 works.** The name therefore receives only an AAAA record.
- **Clients on IPv4-only networks cannot connect** ; this affects some company networks
  and mobile connections.

**How to Recognize DS-Lite:** The router shows no public IPv4 address or an address in
the range `100.64.x.x`–`100.127.x.x`; running `curl -4 https://api.ipify.org` fails,
while the command using `-6` succeeds.

Many providers will switch you to a connection with a real IPv4 address on request at no
charge. If clients need to connect from any network, a call to your provider is the
simplest solution.

### 3.5 Changing Addresses: Dynamic DNS

Residential connections usually have no fixed address: the public IPv4 address or IPv6
prefix changes. Eventually your DNS record points to an outdated address. Three options:

- **Order a fixed address from your provider** – the simplest arrangement, usually for a
  small additional charge.
- **A dynamic DNS service** (dynv6, deSEC, No-IP …), updated by your router whenever the
  address changes. Ensure the service also updates **AAAA records** , which is essential
  with DS-Lite.
- **Run only on your local network** – no dynamic DNS or port access required (see 3.8).

### 3.6 Create Records with Your DNS Provider

Create a record for the desired name with your domain's DNS provider: `A` with the
connection's public IPv4 address and/or `AAAA` with the server's IPv6 address. During
testing, use a short TTL (e.g. `600` seconds) and increase it once everything works.

### 3.7 Check Name Resolution

```bash
getent hosts server.ihrefirma.de

# Ausführlicher, falls installiert (sonst: sudo apt install -y dnsutils)
dig +short A    server.ihrefirma.de
dig +short AAAA server.ihrefirma.de
```

> **A Pitfall on Your Own Network:** Many routers, including FRITZ!Box, use *DNS rebind
> protection* to filter responses pointing to the local network. The name may then fail to
> resolve specifically **on your home network** while working perfectly from outside. Add
> the host name as an exception under **Heimnetz → Netzwerk → Netzwerkeinstellungen →
> DNS-Rebind-Schutz (Home Network → Network → Network Settings → DNS Rebind Protection)**
> . Then clear the test computers' DNS caches so they pick up the change.

### 3.8 Run Only on Your Local Network

If all participants are on the same network, no public DNS or port access is needed. The
name must still exist because the certificate and license rely on it: use internal DNS
or, for small tests only, an entry in `/etc/hosts` on every client:

```text
192.168.1.50    server.ihrefirma.de
```

---

## 4. Router, Port Access and Firewall

### 4.1 IPv4: Port Forwarding

With IPv4, all devices share a public address. The router must know where to forward
incoming connections. Create TCP forwarding rules for `8888`, `8889` and, if used,
`8890` to the server. On a FRITZ!Box: **Internet → Freigaben → Portfreigaben → Gerät für
Freigaben hinzufügen (Internet → Permit Access → Port Sharing → Add Device for
Sharing)**.

### 4.2 IPv6: Allow Access Rather than Forwarding

IPv6 connections are not redirected; each device already has an externally addressable
IP. The router merely opens its firewall for that device and port. The interface usually
still calls this “port sharing”, with an additional choice such as “internet access via
IPv6”.

> Create the access rule **for the device** rather than a manually entered address, so it
> survives a prefix change. With DS-Lite, only IPv6 access rules are effective.

### 4.3 Port 80: Only for Let's Encrypt

Let's Encrypt must verify that you control the name. The simplest method uses port 80,
which needs to remain accessible because renewal runs automatically at regular
intervals. Nothing listens on port 80 between renewals; router warnings here refer to a
continuously running web server.

### 4.4 The Server's Own Firewall

First check whether a firewall is active:

```bash
sudo ufw status
```

If UFW is active, allow the ports:

```bash
sudo ufw allow 8888/tcp
sudo ufw allow 8889/tcp
sudo ufw allow 8890/tcp   # nur bei Browser-Viewer
sudo ufw allow 80/tcp     # nur bei Let's Encrypt
```

> **Take Care with Remote Servers:** Never enable a firewall without first allowing `sudo
> ufw allow OpenSSH` . Otherwise you lock yourself out and must use the provider's console
> to regain access.

---

## 5. The TLS Certificate (PFX File) in Detail

### 5.1 What Is a PFX File?

`cert.pfx` is a password-protected PKCS#12 container holding the **server certificate**
for your server name, the corresponding **private key** and usually the issuing
authority's **intermediate certificates** . FsRdpServer uses it to encrypt all
connections. The service does not start without a valid certificate.

> **The PFX File Is a Secret.** Anyone with the file and its password can impersonate your
> server. Do not send it by unencrypted email, put it in a repository or make it readable
> by all users.

The file must contain **exactly one** certificate with a private key and include the
licensed server name as a *Subject Alternative Name* , either directly
(`server.ihrefirma.de`) or through a wildcard certificate (`*.ihrefirma.de`).

### 5.2 Option A: Bundle a Purchased Certificate into a PFX File

Providers usually supply two or three files: the certificate, private key and
intermediate certificates. Create a PFX file as follows:

```bash
openssl pkcs12 -export -out cert.pfx \
  -inkey privkey.pem -in cert.pem -certfile chain.pem
```

OpenSSL asks for the password interactively. Do not put it on the command line using
`-password pass:...` , where it would be saved in your shell history.

### 5.3 Option B: Free with Let's Encrypt

This works once your server name is in public DNS and port 80 is reachable from outside.
Two commonly used tools:

- **`lego`** – a single program that can create the PFX file directly. This avoids
  conversion, so it is used here.
- **`certbot`** – the best-known client. It creates PEM files that must be bundled as in
  5.2. Note: Debian 13 (“trixie”) and Raspberry Pi OS versions based on it no longer
  include `certbot` as a package.

#### Step 1: Install lego

```bash
sudo apt install -y lego
```

If `apt` cannot find the package, it may be available only through backports in your
version (`sudo apt install -y -t stable-backports lego`), or you can download the
prebuilt program from `go-acme.github.io/lego` .

#### Step 2: Try the Staging Environment First

Let's Encrypt limits failed attempts. First use the staging server to verify that port
access actually works; this does not consume your production quota:

```bash
sudo lego --accept-tos --email ihre.adresse@example.com \
  --domains server.ihrefirma.de \
  --http --http.port :80 --path /etc/lego-staging \
  --server https://acme-staging-v02.api.letsencrypt.org/directory run
```

#### Step 3: Obtain the Real Certificate as PFX

If the test succeeds, obtain the real certificate. Choose a password for the PFX file:

```bash
sudo lego --accept-tos --email ihre.adresse@example.com \
  --domains server.ihrefirma.de \
  --http --http.port :80 --path /etc/lego \
  --pfx --pfx.format SHA256 --pfx.pass 'IhrPasswort' run
```

The resulting file is at `/etc/lego/certificates/server.ihrefirma.de.pfx`.

### 5.4 Option C: Your Company's Own CA

Only suitable for internal installations: every client must explicitly trust your
company CA or all connections fail. This option is unsuitable for the browser viewer
because every browser would show a warning page.

### 5.5 Check the PFX File before Installation

```bash
# Inhalt anzeigen (fragt nach dem Passwort)
openssl pkcs12 -info -in cert.pfx -nokeys

# Namen und Laufzeit des Zertifikats prüfen
openssl pkcs12 -in cert.pfx -clcerts -nokeys -passin pass: 2>/dev/null \
  | openssl x509 -noout -subject -dates -ext subjectAltName
```

Ensure the licensed name appears in `subjectAltName` and the date in `notAfter` is in
the future.

---

## 6. Install the Server Package

### Step 1: Copy the Package to the Server and Extract It

```bash
unzip FsRdpServer-linux-x64.zip -d FsRdpServer
cd FsRdpServer
ls
```

On Raspberry Pi, use `FsRdpServer-linux-arm64.zip`instead. The folder must contain,
among other files, `FsRdpServer`, `install.sh`, `fsrdp-server.service` and
`README-LINUX.md` .

### Step 2: Run the Installer

```bash
chmod +x FsRdpServer install.sh
sudo ./install.sh
```

On first installation, the service intentionally remains **stopped**until the
certificate is configured. This is not an error. No license is needed to start; without
one, the 30-day trial described in section 7.

**What Is Created:**

| Pfad | Bedeutung |
|---|---|
| `/opt/fsrdp-server/FsRdpServer` | the program itself |
| `/opt/fsrdp-server/data/` | configuration, certificate, license, installation ID and logs |
| `/etc/fsrdp-server/fsrdp-server.env` | secrets, readable only by `root` . |
| `/etc/systemd/system/fsrdp-server.service` | the systemd service definition |
| User `fsrdp-server` | dedicated restricted service account with no sign-in capability |

To use ports other than `8888`/`8889` , set them in
`/opt/fsrdp-server/data/appsettings.json` . Afterwards restore ownership and
permissions: `sudo chown fsrdp-server:fsrdp-server …` and `sudo chmod 0640 …`.

---

## 7. Trial Period or License (Generate the Installation ID)

### No License Needed for the First 30 Days

If there is no license file in the data directory, the server still starts and runs **as
a trial installation for 30 days**. Nothing needs to be enabled: install the package,
configure the certificate and PFX password (section 8), then start the service. The
trial limits duration and capacity, **not** features:

|  | Trial Period | With a License |
|---|---|---|
| Concurrent Rooms | 1, fixed number `123456789` | as specified in the license |
| Session Duration | up to 24 hours | as specified in the license |
| Waiting Time Afterwards | none | as specified in the license |
| Participants per Room | 5 | as specified in the license |
| Room Features | all | all |
| Total Duration | 30 days from installation | license term |
| Watermark | yes | no |

“All features” means screen sharing, remote control, camera, voice, chat, file transfer,
session recording, live captions and the browser viewer. Their technical requirements
still apply: captions need your own API key, and the browser viewer needs its port and
certificate (section 10).

During the trial, all participants see a **Watermark** over the shared screen. This is
intentional: an unlicensed server should be recognizable as such.

What the trial does not include is **supporter key** and the features tied to those
keys: reserved room names and the room password with which a customer opens their
permanent room in a browser. This is not a feature block; an unlicensed server simply
does not issue keys.

> **The room number `123456789` is publicly known.** If your test server is reachable from
> the internet, set a **room PIN**. The field is next to the room number in both desktop
> clients. The person opening the room sets it; everyone else needs it to join.

After 30 days, the service keeps running and listening but responds to every connection
with a request for a license; it does not refuse **not** to start. You can install a
license file later at any time (section 12).

### Request a License

For continued operation, you need a signed license file. It is tied to your **server
name** and a permanent **installation ID** . Unlike Windows, on Linux you generate this
ID yourself *before* requesting the license, exactly once:

```bash
sudo -u fsrdp-server env \
  FSRDP_DATA_DIR=/opt/fsrdp-server/data \
  /opt/fsrdp-server/FsRdpServer --installation-id
```

The command prints a long string and saves it in
`/opt/fsrdp-server/data/server-instance-id.txt`.

Send Me These Details for Issuing the License:

- the generated installation ID;
- the exact DNS name clients will use;
- the number of concurrent rooms and participants per room;
- the desired term;
- whether you want browser viewing, session recording or live captions; these features
  must be enabled in the license.

> **`server-instance-id.txt` must never be lost** , including during updates or
> reinstallation. If the file is lost, your license no longer matches and must be
> reissued.

---

## 8. Install the Certificate, License and Password

### Step 1: Store Both Files with the Correct Permissions

Change to the folder containing `license.fsrdp` and `cert.pfx` :

```bash
sudo install -o fsrdp-server -g fsrdp-server -m 0640 \
  license.fsrdp /opt/fsrdp-server/data/license.fsrdp

sudo install -o fsrdp-server -g fsrdp-server -m 0640 \
  cert.pfx /opt/fsrdp-server/data/cert.pfx
```

`install` copies the file and sets ownership and permissions in one step; the service
account can read it, ordinary users cannot.

### Step 2: Store the PFX Password

The password belongs in the environment file readable only by `root` , rather than the
configuration file:

```bash
sudoedit /etc/fsrdp-server/fsrdp-server.env
```

Enter these lines, replacing existing entries:

```ini
FSRDP_CERT_PASSWORD=hier-das-echte-passwort
```

Then ensure the correct permissions:

```bash
sudo chown root:root /etc/fsrdp-server/fsrdp-server.env
sudo chmod 0600 /etc/fsrdp-server/fsrdp-server.env
```

If the password contains spaces or special characters, quote it according to systemd
syntax. The simplest approach is to avoid both and use a long password of letters and
digits.

### Step 3: Check File Permissions

```bash
sudo stat -c '%U:%G %a %n' \
  /opt/fsrdp-server/FsRdpServer \
  /opt/fsrdp-server/data \
  /opt/fsrdp-server/data/cert.pfx \
  /opt/fsrdp-server/data/license.fsrdp \
  /etc/fsrdp-server/fsrdp-server.env \
  /etc/systemd/system/fsrdp-server.service
```

Expected values:

| Pfad | Bedeutung |  |
|---|---|---|
| Path | Owner | Permissions |
| `/opt/fsrdp-server/FsRdpServer` | `root:root` | `0755` |
| `/opt/fsrdp-server/data` | `fsrdp-server:fsrdp-server` | `0750` |
| `/opt/fsrdp-server/data/cert.pfx` | `fsrdp-server:fsrdp-server` | `0640` |
| `/opt/fsrdp-server/data/license.fsrdp` | `fsrdp-server:fsrdp-server` | `0640` |
| `/etc/fsrdp-server/fsrdp-server.env` | `root:root` | `0600` |
| `/etc/systemd/system/fsrdp-server.service` | `root:root` | `0644` |

---

## 9. Validate and Start the Service

### 9.1 Self-Check before the First Start

The following command looks complex for a reason: it lets systemd load the password file
readable only by `root` while running validation under the service account. The PFX
password never needs to appear on the command line.

```bash
sudo systemd-run \
  --quiet --wait --pipe --collect \
  --property=User=fsrdp-server \
  --property=Group=fsrdp-server \
  --property=EnvironmentFile=/etc/fsrdp-server/fsrdp-server.env \
  --property=Environment=FSRDP_DATA_DIR=/opt/fsrdp-server/data \
  /opt/fsrdp-server/FsRdpServer --validate-installation
```

Successful output confirms that the license, installation ID and certificate match. A
**DNS warning** is acceptable only if you intentionally provide the name through
internal DNS or hosts files; clients must still be able to resolve it. Fix every genuine
error before starting.

### 9.2 Start the Service

```bash
sudo systemctl start fsrdp-server.service
systemctl is-active fsrdp-server.service
systemctl is-enabled fsrdp-server.service
```

`is-active` must report `active` , and `is-enabled` must report `enabled` . The latter
ensures the service starts automatically after a power failure.

### 9.3 Check

```bash
# Lauschen die Ports?
sudo ss -lntp | grep -E ':(8888|8889|8890)\b'

# Was sagt das Protokoll?
sudo journalctl -u fsrdp-server.service -n 50 --no-pager

# Lizenzstatus im Klartext
sudo -u fsrdp-server env FSRDP_DATA_DIR=/opt/fsrdp-server/data \
  /opt/fsrdp-server/FsRdpServer --license-status
```

During the trial, you see this instead of customer details:

```text
License state:         trial
Trial state file:      /opt/fsrdp-server/data/trial-state.json
Trial period:          2026-09-11 through 2026-10-11 (30 day(s) left)
Trial room:            123456789
```

The 30-day period is tied to the **first start of this installation**, recorded in
`trial-state.json`. It is measured against the latest time ever observed; turning the
system clock back does not extend it.

The startup log shows which certificate was loaded and its expiry date, among other
details. This is a useful first place to look if something stops working later.

---

## 10. Browser Viewer on Port 8890 with Its Own Certificate

The browser viewer lets viewers participate without an installed client; they simply
open `https://server.ihrefirma.de:8890/`. It is disabled by default and requires two
settings: permission **in the license** and activation **in the configuration**.

### 10.1 Why a Second Certificate Makes Sense

Any certificate trusted by native clients is sufficient for them, including one from
your own company CA. A **Browser** is stricter and shows visitors a warning if the
certificate is not issued by a publicly recognized authority. The browser viewer can
therefore have a separate certificate without changing the native channels' certificate.
If left empty, `cert.pfx` is used automatically.

### 10.2 Setup

#### Step 1: Obtain a Publicly Trusted Certificate

Set up with `lego` as described in section 5.3. Remember the PFX password you chose.

#### Step 2: Copy the Certificate to the Data Directory

```bash
sudo install -o fsrdp-server -g fsrdp-server -m 0640 \
  /etc/lego/certificates/server.ihrefirma.de.pfx \
  /opt/fsrdp-server/data/webgateway-cert.pfx
```

#### Step 3: Add the Password to the Environment File

```bash
sudoedit /etc/fsrdp-server/fsrdp-server.env
```

Add this alongside the existing line:

```ini
FSRDP_WEB_CERT_PASSWORD=das-passwort-der-webgateway-pfx
```

#### Step 4: Update Configuration

```bash
sudoedit /opt/fsrdp-server/data/appsettings.json
```

Set or add two values:

```json
"WebGatewayEnabled": true,
"WebGatewayCertificatePath": "webgateway-cert.pfx",
```

A relative path is relative to the data directory. Afterwards restore permissions: `sudo
chown fsrdp-server:fsrdp-server /opt/fsrdp-server/data/appsettings.json` and `sudo chmod
0640 …`.

#### Step 5: Restart and Check

```bash
sudo systemctl restart fsrdp-server
sudo journalctl -u fsrdp-server -n 30 --no-pager | grep -i certificate
```

The log must now contain **two** certificate lines: one for the native channels and one
for the browser viewer. Finally check the chain from outside:

```bash
echo | openssl s_client -connect server.ihrefirma.de:8890 \
  -servername server.ihrefirma.de 2>&1 | grep -E 'Verify return code'
```

`Verify return code: 0 (ok)` means the browser will not show a warning.

### 10.3 Set Up Automatic Renewal

> The server reads its certificates **only at startup**. Renewal takes effect after
> restarting the service, which disconnects all active sessions. The renewal script should
> therefore restart the service only when the certificate actually changes.

Have `/usr/local/sbin/fsrdp-renew-webcert.sh` :

```bash
#!/bin/bash
set -euo pipefail

domain=server.ihrefirma.de
email=ihre.adresse@example.com
env_file=/etc/fsrdp-server/fsrdp-server.env
lego_pfx="/etc/lego/certificates/${domain}.pfx"

pfx_pass=$(sed -n 's/^FSRDP_WEB_CERT_PASSWORD=//p' "$env_file")
[ -n "$pfx_pass" ] || { echo "Passwort fehlt in $env_file" >&2; exit 1; }

before=$(sha256sum "$lego_pfx" 2>/dev/null | cut -d' ' -f1 || true)

lego --accept-tos --email "$email" --domains "$domain" \
  --http --http.port :80 --path /etc/lego \
  --pfx --pfx.format SHA256 --pfx.pass "$pfx_pass" \
  renew --days 30

after=$(sha256sum "$lego_pfx" | cut -d' ' -f1)
[ "$before" != "$after" ] || { echo "Zertifikat unveraendert."; exit 0; }

install -o fsrdp-server -g fsrdp-server -m 0640 \
  "$lego_pfx" /opt/fsrdp-server/data/webgateway-cert.pfx
systemctl restart fsrdp-server
echo "Neues Zertifikat eingespielt, Dienst neu gestartet."
```

Make It Executable and Schedule It:

```bash
sudo chmod 0750 /usr/local/sbin/fsrdp-renew-webcert.sh
sudo systemctl edit --force --full fsrdp-webcert-renew.service
```

Service file contents:

```ini
[Unit]
Description=Zertifikat des FsRdp-Browser-Viewers erneuern
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/fsrdp-renew-webcert.sh
```

And the corresponding timer:

```bash
sudo systemctl edit --force --full fsrdp-webcert-renew.timer
```

```ini
[Unit]
Description=Zweimal taeglich pruefen, ob das Zertifikat erneuert werden muss

[Timer]
OnCalendar=*-*-* 03,15:00:00
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
```

Enable and test manually once:

```bash
sudo systemctl enable --now fsrdp-webcert-renew.timer
sudo systemctl start fsrdp-webcert-renew.service
sudo journalctl -u fsrdp-webcert-renew.service -n 20 --no-pager
```

---

## 11. Your First Connection Test

Do **not** test from the server itself, since this would miss router and firewall
problems. Install the client on another device (see [Client Installation
Guide](https://www.fssoft.de/FsRdp/en/InstallingClient.aspx)), enter the licensed server
name and connect two devices to the same room. Only with two participants can you verify
that screen and audio are transmitted correctly.

> **Do not run `telnet` or `nc` in a loop.** The server treats repeated connection
> attempts without a valid protocol as an attack and temporarily blocks the test
> computer's IP address.

---

## 12. Ongoing Operation

### Backups

Back up `/opt/fsrdp-server/data` and `/etc/fsrdp-server/fsrdp-server.env` to a location
with equally restrictive access permissions, since they contain the PFX file and
password. Most important is `server-instance-id.txt`: without it, your license is
unusable for this installation.

### Update the Server

```bash
unzip FsRdpServer-linux-x64.zip -d FsRdpServer-neu
cd FsRdpServer-neu
chmod +x FsRdpServer install.sh
sudo ./install.sh
```

If the service was running, the installer stops it, replaces the program and service
definition, then restarts it. Certificate, password, license, installation ID, room
reservations, configuration and logs remain unchanged.

> **A Deployment Pitfall:** If `/etc/fsrdp-server/fsrdp-server.env`is missing, the
> installer creates it from the template with a placeholder password. Since the
> environment variable takes precedence over the configuration file, the service will then
> fail to start. Restore the file with the real password *before* installing an update;
> the installer leaves an existing file unchanged.

### Install or Replace the License

The same procedure applies to the first license after a trial and a renewed license. The
license itself does not require restarting the service, which reloads it automatically.
Browser viewing, captions and demo mode settings are read at startup and do require a
restart.

```bash
sudo systemctl stop fsrdp-server.service
sudo install -o fsrdp-server -g fsrdp-server -m 0640 \
  license.fsrdp /opt/fsrdp-server/data/license.fsrdp
# Danach die Selbstpruefung aus Abschnitt 9.1 wiederholen
sudo systemctl start fsrdp-server.service
```

Do not start the service if validation fails. If a license is regenerated with a
different supporter count, all supporter keys may change; distribute new keys promptly.

### Replace the Certificate

```bash
sudo install -o fsrdp-server -g fsrdp-server -m 0640 \
  cert.pfx /opt/fsrdp-server/data/cert.pfx
# Falls sich das Passwort geaendert hat: fsrdp-server.env anpassen
sudo systemctl restart fsrdp-server.service
```

### Connect the Pi only while it is needed

A server that is used now and then – a demo machine, a stand-by, a Pi that carries one
support session a week – does not have to sit on the network for the rest of the time.
Plug it in about two minutes beforehand; that is enough for it to boot, and the service
starts on its own. What cannot be reached cannot be scanned or attacked, and the port
forwarding for 8888, 8889 and 8890 exposes the Pi to the entire Internet for as long as
it hangs on the cable.

What matters is how it goes off: by the button, not by the plug. A Raspberry Pi 5 has a
button on the board – a short press shuts it down cleanly, and a press on a halted Pi
starts it again. Older models get the same behaviour from `dtoverlay=gpio-shutdown` in
config.txt together with a momentary switch between pin 5 (GPIO3) and pin 6 (GND). Over
SSH, `sudo poweroff` does the same. Pull the plug once the activity LED has gone quiet.

> **Why it matters:** Raspberry Pi OS journals its file system, so most abrupt power cuts
> pass without a trace. The risk per occurrence is small, but it accumulates – and it
> lands on the card that also carries the license, the certificate, the installation ID
> and the room reservations. Booting from a USB SSD instead of an SD card takes the
> weakest part out of the picture altogether.

---

## 13. Common Problems

### The Service Does Not Start

First check the log: `sudo journalctl -u fsrdp-server -n 50 --no-pager`. Common causes
include an incorrect PFX password, a configuration syntax error (even a missing comma)
and insufficient read permissions for the service account.

### Permission Denied

Restore ownership and permissions from section 8. The PFX file and license must not be
readable by all users, but the service account must be able to read them.

### The Certificate Name Does Not Match

The client name, certificate name and licensed host name differ. Check all three using
the command from 5.5 and always connect using the licensed name.

### Every Client Reports “Trial Period Expired”

The 30 days have ended. The service keeps running but accepts no connections until a
license is installed (section 12). `--license-status` shows `trial-expired` and the end
date in this case.

### The Server Remains in Trial Mode Despite an Installed License

The license file was not found or accepted. The startup banner in the journal shows both
the expected path and the reason. Typical causes are an incorrect `LicensePath`,
insufficient service account read permissions for `license.fsrdp`, a license for another
installation ID or a manually edited and therefore invalid file.

### The Service Runs, but No Clients Connect

1. Does the client's name resolution return the correct address?
2. Is the router's DNS rebind protection interfering (see 3.7)?
3. Is router access enabled, including IPv6 with DS-Lite?
4. Is the server listening? `sudo ss -lntp | grep 8888`
5. Is UFW blocking access? `sudo ufw status`

### The Browser Shows a Certificate Warning

The browser viewer is still using the native channels' certificate. Configure a publicly
trusted certificate as described in section 10.

### It Worked before, but Not Today

Two common causes: the certificate expired, or your connection's public address changed
and the DNS record is outdated. An IPv6 prefix change affects both the DNS record and
the router access rule.

---

## Further Reading

- [More on PFX Certificates: Obtaining, Installing and
  Renewing](https://www.fssoft.de/FsRdp/en/HowToPFX.aspx)
- [The Same Guide for Windows
  Servers](https://www.fssoft.de/FsRdp/en/InstallingServerWindows.aspx)
- [Install the Client](https://www.fssoft.de/FsRdp/en/InstallingClient.aspx)
- [License Terms](https://www.fssoft.de/FsRdp/en/Lizenz.aspx)

**Stuck at a Particular Step?** Get in touch; we are happy to agree on the server name,
certificate and license together.

Robert Fischbacher

[Contact Me Directly](https://www.fssoft.de/en/Impressum.aspx)
