Platform Solutions Mobile Integrations Trust & Architecture Docs Contact

Portal Installation Guide

Build the CycleOps Portal server on VMware ESXi: create the virtual machine, install Ubuntu Server 26.04 LTS, install the Portal, and connect it to the main server.

Ubuntu Server 26.04.1 LTS · VMware ESXi 7.0 U2+
Step 1

Prerequisites

This guide covers building the server that runs the CycleOps Portal, from an empty ESXi host to a Portal connected to the main server. The Portal server is a separate virtual machine from the main server; its address is the value the main installer asks for as the DMZ portal server IP.

Have the following ready before you start:

  • A VMware ESXi host on version 7.0 U2 or later, and an account allowed to create virtual machines.
  • At least 100 GB of free datastore space.
  • The ubuntu-26.04.1-live-server-amd64.iso image (2.73 GB) uploaded to a datastore. See Download the installation image in the main server guide.
  • A static IP address, subnet, default gateway, and DNS server addresses for the server.
  • HTTP access (TCP 80) from the server to archive.ubuntu.com, or the address of your proxy.
  • The items listed in step 7.1 for the Portal installation itself.

In the installer, move with the arrow keys and Tab, toggle an option with Space, and confirm with Enter.

Step 2

Installation parameters

These are the values used throughout this guide. The network values are examples; replace them with the values for your own network.

ParameterValueNote
Virtual machine nameCycleOps-Portal
CompatibilityESXi 7.0 U2 virtual machine
Guest OSLinux / Ubuntu Linux (64-bit)
vCPU8
Memory16 GB
Disk100 GBSCSI controller: VMware Paravirtual
Network adapterVMXNET 3Port group: LAN
Hostnamecycleops-portal
Administrator usercoadminMember of sudo
Network interfaceens192
Subnet10.0.1.0/24Example value
IP address10.0.1.191Example value
Gateway10.0.1.1Example value
DNS servers8.8.8.8, 8.8.4.4Example value; use your internal DNS if you have one
Partition: swap8 GB
Partition: /ext4, all remaining spaceAbout 91 GB
Step 3

Create the virtual machine

The virtual machine is created with the five-page New virtual machine wizard in ESXi Host Client.

3.1 Start the wizard

Sign in to ESXi Host Client. In the Navigator, right-click Virtual Machines and select Create/Register VM.

Create/Register VM in the Navigator menu
Create/Register VM in the Navigator menu

3.2 Select the creation type

Select Create a new virtual machine and continue with NEXT.

Select creation type
Select creation type

3.3 Set the name and guest OS

Enter CycleOps-Portal as the name. Select ESXi 7.0 U2 virtual machine for Compatibility, Linux for Guest OS family, and Ubuntu Linux (64-bit) for Guest OS version.

Select a name and guest OS
Select a name and guest OS

3.4 Select the datastore

Select the datastore that will hold the virtual machine files. It needs at least 100 GB of free space.

Select storage
Select storage

3.5 Configure the hardware

On the Virtual Hardware tab, set CPU to 8, Memory to 16 GB, and Hard disk 1 to 100 GB. Keep SCSI Controller 0 on VMware Paravirtual, and select the port group the server connects to for Network Adapter 1.

CPU, memory, and disk values in Customize settings
CPU, memory, and disk values in Customize settings

3.6 Attach the installation image

In the CD/DVD Drive 1 row, select Datastore ISO file. In the Datastore browser, locate ubuntu-26.04.1-live-server-amd64.iso and confirm with SELECT.

Selecting Datastore ISO file for CD/DVD Drive 1
Selecting Datastore ISO file for CD/DVD Drive 1

Expand CD/DVD Drive 1 and confirm that Connect at power on is checked and the image path appears under CD/DVD Media. Continue with NEXT.

Image path under CD/DVD Media with Connect at power on checked
Image path under CD/DVD Media with Connect at power on checked

3.7 Review and finish

Compare the summary on the Ready to complete page with your parameters, then create the virtual machine with FINISH.

Ready to complete summary
Ready to complete summary

3.8 Power on

Open CycleOps-Portal from the virtual machine list, select Power on, and open the Console. The "VMware Tools is not installed" notice is expected at this stage.

CycleOps-Portal summary with the Power on button
CycleOps-Portal summary with the Power on button
Step 4

Install Ubuntu Server 26.04

The operating system is installed from the virtual machine console with the Ubuntu installer.

4.1 Start the installer

With Try or Install Ubuntu Server highlighted in the GRUB menu, press Enter. Loading the installer can take a minute or two.

Try or Install Ubuntu Server in the GRUB menu
Try or Install Ubuntu Server in the GRUB menu

4.2 Language and keyboard

Select English as the language. On the keyboard page, select the layout that matches your keyboard and continue with Done.

Language selection
Language selection
Keyboard configuration
Keyboard configuration

4.3 Installation type

Keep Ubuntu Server selected. Leave "Search for third-party drivers" unchecked and continue with Done.

Choose the type of installation
Choose the type of installation

4.4 Network configuration

On the Network configuration page, highlight the ens192 interface, press Enter, and select Edit IPv4.

Edit IPv4 on the ens192 interface
Edit IPv4 on the ens192 interface

Select Manual as the IPv4 Method. Fill in Subnet, Address, Gateway, and Name servers with your network values, separate DNS addresses with commas, and select Save.

Static IPv4 values
Static IPv4 values

After saving, confirm reachability by pinging the server address from another computer on the network.

Successful ping to the server address
Successful ping to the server address

When the interface row shows static and the address you assigned, continue with Done.

ens192 with the static address assigned
ens192 with the static address assigned

4.5 Proxy and package mirror

If the server reaches the internet through a proxy, enter it as http://host:port. Otherwise leave the field empty and continue with Done.

Proxy configuration
Proxy configuration

Leave the mirror address at http://archive.ubuntu.com/ubuntu/. When "This mirror location passed tests." appears, continue with Done.

Ubuntu archive mirror configuration
Ubuntu archive mirror configuration

4.6 Select the storage layout

On the Guided storage configuration page, select Custom storage layout and continue with Done.

Custom storage layout selected
Custom storage layout selected

4.7 Create the swap partition

Under AVAILABLE DEVICES, highlight free space, press Enter, and select Add GPT Partition.

Add GPT Partition on free space
Add GPT Partition on free space

Enter 8G as the size, select swap as the format, and select Create.

Creating the 8G swap partition
Creating the 8G swap partition

4.8 Create the root partition

Select Add GPT Partition on free space again. Keep the size at all remaining space, select ext4 as the format and / as the mount point, then select Create.

Creating the ext4 root partition with the remaining space
Creating the ext4 root partition with the remaining space

When FILE SYSTEM SUMMARY lists / (ext4), /boot/efi, and SWAP (8G), continue with Done.

Selecting Continue in the Confirm destructive action dialog formats the disk. You cannot return to earlier pages after this point.

Partition summary and the Confirm destructive action dialog
Partition summary and the Confirm destructive action dialog

4.9 User and server name

Enter CycleOps as your name, cycleops-portal as the server name, and coadmin as the username. Choose a password of at least 12 characters with upper and lower case letters, digits, and symbols, then continue with Done.

Profile configuration
Profile configuration

4.10 Ubuntu Pro, SSH, and snaps

On the Upgrade to Ubuntu Pro page, select Skip for now and Continue.

Upgrade to Ubuntu Pro
Upgrade to Ubuntu Pro

On the SSH configuration page, check Install OpenSSH server and continue with Done.

SSH configuration
SSH configuration

On the Featured server snaps page, continue with Done without selecting anything.

Featured server snaps
Featured server snaps

4.11 Wait for the installation to finish

The page shows the installation progress. The installation is finished when the title reads Installation complete! and the Reboot Now button appears at the bottom of the page.

Installation complete page with the Reboot Now button
Installation complete page with the Reboot Now button
Step 5

Post-installation

After the installation, reboot the server, detach the image, and bring the system up to date.

5.1 Reboot and detach the image

Select Reboot Now. If "Please remove the installation medium, then press ENTER" appears, press Enter.

Remove the installation medium prompt
Remove the installation medium prompt

In ESXi Host Client, open Edit for the virtual machine and uncheck Connect and Connect at power on for CD/DVD Drive 1.

5.2 Connect over SSH

Connect from your management computer. On the first connection, answer yes to the host key prompt and enter the user password.

ssh coadmin@10.0.1.191

5.3 Update packages

Update the package lists, upgrade all packages, and reboot.

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

5.4 VMware tools and time zone

Confirm that open-vm-tools is installed, set the time zone, and check time synchronization.

sudo apt install -y open-vm-tools
sudo timedatectl set-timezone Europe/Istanbul
timedatectl

The timedatectl output should show System clock synchronized: yes.

Step 6

Verification checklist

The server is ready for the Portal installation when all six checks return the expected result.

CheckCommandExpected result
OS releaselsb_release -dUbuntu 26.04.1 LTS
Hostnamehostnamectl hostnamecycleops-portal
IP addressip -4 addr show ens192The static address you assigned
CPU and memorynproc; free -h8 cores, about 16 GiB memory, 8 GiB swap
Diskdf -h /ext4 root of about 91 GB
Internet and DNSping -c 3 archive.ubuntu.comReplies received, no packet loss
Step 7

Install the CycleOps Portal

The Portal is installed with a single script, portalinstall.sh. The script installs Docker, downloads and loads the Portal package, creates the certificates and configuration, and starts the services. Two short tasks then follow on the main server.

7.1 What you need

  • A completed main server installation. See the Server Installation Guide.
  • The portalinstall.sh installer, the version tag, and the package token provided by the CycleOps team. This is the same package token used for the main server; the image token is not needed here.
  • HTTPS access (TCP 443) from the Portal server to api.github.com, get.docker.com, download.docker.com, and registry-1.docker.io.
  • Access from the Portal server to TCP 8443 on the main server, for example 10.0.1.190:8443.

7.2 Copy the installer and start it

On your management computer, run this from the folder that contains portalinstall.sh. Any SFTP client, such as WinSCP, works as well.

scp portalinstall.sh coadmin@10.0.1.191:/tmp/

Then start the installation on the Portal server:

sudo bash /tmp/portalinstall.sh

The installer currently prints its messages in Turkish. The terminal output in this section is shown translated to English, in the same order as it appears on screen.

The script runs its pre-checks and installs Docker. All lines must show [OK].

Cycleops DMZ Portal — installation

==> 1/8  Pre-checks
  [OK] OS: Ubuntu 26.04.1 LTS
  [OK] curl · python3 · tar · openssl
  [OK] api.github.com access
==> 2/8  Docker
  [OK] installed
  [OK] coadmin + docker group + passwordless sudo

7.3 Answer the questions and enter the package token

The script asks the following questions in order. The value in square brackets is the default; pressing Enter accepts it.

PromptAnswer
IP address of this DMZ serverEnter; the server address is pre-filled
IP address of the CycleOps MAIN serverThe main server address, for example 10.0.1.190
Distribution repositoryEnter
Version tagThe same version as the main server
GitHub tokenThe package token; the characters are not shown as you paste
==> 3/8  Details
  IP address of this DMZ server [10.0.1.191]:
  IP address of the CycleOps MAIN server: 10.0.1.190
  Distribution repository [sealtinay/cycleops-dist]:
  Version tag (SAME as the main server, e.g. v04ef16a3): v2e14d45a
  [OK] DMZ 10.0.1.191 · main server 10.0.1.190 · v2e14d45a
==> 4/8  Download the Portal package
  Distribution token (classic PAT, repo scope) — NOT the GHCR token.
  GitHub token (not shown on screen):
  downloading: portal-dir.tar.gz (0.0 MB)
  downloading: portal-images.tar.gz (79.5 MB)
  [OK] portal-images.tar.gz + portal-dir.tar.gz
==> 5/8  Extract and load the images
  Loaded image: cycleops/portal-api:0.1.0
  Loaded image: cycleops/portal-web:0.1.0
  [OK] /opt/cycleops-portal ready

The prompt mentions a "classic PAT"; enter the package token provided by the CycleOps team regardless of its type.

7.4 Paste the internal gateway certificate

The Portal connects to the main server over TLS and needs the main server's internal certificate. When the script stops at this prompt, run the following command on the main server:

sudo cat /opt/cycleops/infra/nginx/gateway-internal-certs/gateway-internal.crt

Copy the whole output, including the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- lines, paste it into the Portal installer, and press Ctrl-D. Answer h (no) when asked whether Cloudflare will be used.

==> 6/8  Certificates
  [OK] portal TLS created (5 years)

  Internal gateway certificate required. On the main server:
    sudo cat /opt/cycleops/infra/nginx/gateway-internal-certs/gateway-internal.crt
  Paste the output here:
  Press Ctrl-D after pasting:
-----BEGIN CERTIFICATE-----
MIIF1jCCA76gAwIBAgIU...
...
-----END CERTIFICATE-----
  [OK] upstream CA written
  Will Cloudflare be used? (e/h) [h]: h
  [OK] permissions (644/755 for AppArmor)

7.5 Complete the configuration

The configuration stage asks a few more questions. The customer code and display name are required.

PromptAnswer
Main server address (https://IP:8443)Enter; the address is pre-filled
IP of the DMZ server (for CORS)Enter
Customer codeLowercase letters, digits, and hyphens, for example customer
Customer display nameFor example CUSTOMER
Will Cloudflare be used?h (no)
==> 7/8  Configuration
Internal gateway (CycleOps main server)
  NOTE: port 8443 — NOT 443.
  Main server address (https://IP:8443) [https://10.0.1.190:8443]:

This portal server
  IP of the DMZ server (for CORS) [10.0.1.191]:
  Customer code (lowercase letters/digits/hyphens): customer
  Customer display name: CUSTOMER

  Shared token generated (write the SAME value to the internal gateway .env):
    GATEWAY_PORTAL_SHARED_SECRET=<SHARED_TOKEN>

Cloudflare (mobile app / roaming agent relay)
  Will Cloudflare be used? (e/H): h
== .env written ==
  [OK] .env ready
==> 8/8  Start the services
  [OK] portal-nginx recreated
  containers:
portal-nginx  Up 8 seconds
portal-api  Up 9 seconds (healthy)
portal-redis  Up 20 seconds (healthy)
portal-web  Up 20 seconds (healthy)
  https://localhost : 200

All four containers must be Up and the last line must read https://localhost : 200. The script then prints the commands to run on the main server, with your shared token already filled in.

7.6 Register the Portal on the main server

Run both tasks on the main server. Until they are done, the Portal cannot connect to the main server.

First, add the shared token and recreate the gateway. Replace <SHARED_TOKEN> with the value the Portal installer printed:

echo 'GATEWAY_PORTAL_ENABLED=true' | sudo tee -a /opt/cycleops/.env >/dev/null
echo 'GATEWAY_PORTAL_SHARED_SECRET=<SHARED_TOKEN>' | sudo tee -a /opt/cycleops/.env >/dev/null
sudo grep '^GATEWAY_PORTAL' /opt/cycleops/.env
cd /opt/cycleops/infra && sudo docker compose up -d --force-recreate gateway

The grep command must show exactly two lines.

Second, allow the Portal server's address:

sudo cycleops portal allow 10.0.1.191
sudo cycleops portal list

During the configuration stage the installer also suggests writing an allow line to local.conf and restarting nginx. Use cycleops portal allow instead: it validates the address and the nginx configuration and reloads without downtime.

7.7 Verify the connection

Run these commands on the Portal server:

T=$(sudo grep '^PORTAL_UPSTREAM_SHARED_TOKEN=' /opt/cycleops-portal/.env | cut -d= -f2)
curl -sk -o /dev/null -w 'without token: %{http_code}\n' https://10.0.1.190:8443/v1/service-desk/portal/tickets
curl -sk -o /dev/null -w 'with token   : %{http_code}\n' -H "X-Portal-Token: $T" https://10.0.1.190:8443/v1/service-desk/portal/tickets
without token: 403
with token   : 422

The 403 shows that requests without the token are rejected, and the 422 shows that the Portal reaches the main server with its token. If the request with the token also returns 403, the allow step in 7.6 has not been applied.

Finally, open https://10.0.1.191 (your Portal address) in a browser. A browser security warning is expected with a self-signed certificate.

Installation complete. The Portal is now reachable from your internal network. Internet access, the mobile app, and agents outside the corporate network additionally require the Cloudflare configuration, which will be covered in a separate guide.

Questions during the installation? Write to support@cycleops.ai.