Installation and Configuration of sa-ai
Overview
sa-ai is an LLM inference service for Search Anywhere (prefix sa). The installer deploys the service as a systemd unit, generates runtime configuration, prepares TLS materials, and optionally configures an account and keystore entry in local OpenSearch.
Conditional Designations
SA_AI_HOME— service installation directory (default/app/sa-ai)SA_AI_LOGS— logs directory (default/app/logs/sa-ai)OS_HOME— OpenSearch installation directory
Prerequisites
Before running the installer, ensure the following conditions are met on the host.
Required Dependencies
openssl— for working with certificatescurl— for OpenSearch API requestssystemd— for service management- Java 21 or newer — if not using the bundled JDK from the package (more details in section JDK Selection)
Installation Package Contents
Before running, check for the following files:
sa-ai-installer/
├── setup.sh ← entry point
├── artifacts/
│ ├── jar/
│ │ └── sa-ai-<version>.jar ← service JAR (required)
│ └── source/
│ └── jdk-*.tar.gz ← bundled JDK (needed if JDK_SOURCE: bundled)
├── configs/
│ └── model_registry.json
├── defaults/
├── lib/
├── templates/
└── example_config_sa-ai.yaml
The file artifacts/jar/sa-ai-<version>.jar must be present before starting installation. Without it, the installer will fail at the pre-flight step.
TLS Certificates
If planning to use existing certificates (CERT_MODE: provided), check for:
- HTTPS certificate for the
sa-aiservice (sa-ai-cert.pem) - Private key (
sa-ai-key.pem) - OpenSearch CA certificate (
ca-cert.pem) — needed even with HTTPS disabled, sincesa-aialways verifies OpenSearch's TLS certificate
If planning to generate a certificate (CERT_MODE: generate), check for:
- OpenSearch CA certificate (
ca-cert.pem) - CA private key (
ca-key.pem)
Installation Package Structure
| Path | Purpose |
|---|---|
setup.sh | Single entry point |
lib/cmd_install.sh | Installation logic |
lib/cmd_update.sh | Update logic |
lib/cmd_uninstall.sh | Uninstallation logic |
defaults/functions.sh | Common shell functions library |
defaults/default.env | Default values (documentation etc.) |
templates/application.properties | Runtime configuration template |
templates/sa-ai.service | Systemd unit template |
configs/model_registry.json | Base model registry |
example_config_sa-ai.yaml | Sample YAML configuration |
artifacts/jar/ | Directory for sa-ai-<version>.jar |
artifacts/source/ | Directory for JDK archive |
Installation Methods
The installer supports three modes:
| Mode | Command | When to Use |
|---|---|---|
| Interactive | sudo ./setup.sh install | One-time deployment |
| YAML Configuration | sudo ./setup.sh install -c config.yaml | Automation |
| Rootless | ./setup.sh install --no-root | Limited sudo access |
Interactive Installation
Launch
sudo ./setup.sh install
1. Pre-flight Checks
The installer automatically checks:
- JAR presence in
artifacts/jar/ - Run permissions (root or sudo)
- Presence of
openssl,curl,systemd
No intervention required. If an error occurs, the installer displays the reason and exits.
2–3. Environment Detection
The installer automatically detects:
- Presence of local OpenSearch (
opensearch.service) - Availability of OpenSearch JDK
- System Java in
PATH - Bundled JDK in
artifacts/source/
Then displays a summary and offers to select a mode:
Run in no-root mode? (print root commands) [y/N; default N]:
Enter y if you have limited sudo, or press Enter to continue in normal mode.
4. Deployment Parameters
The installer requests paths and service settings. Default values are shown in brackets — press Enter to accept.
sa-ai home directory [/app/sa-ai]:
sa-ai logs directory [/app/logs/sa-ai]:
Service port [8010]:
Linux service user [opensearch]:
If OpenSearch is not installed on this host, additional information is requested:
OpenSearch host (for sa-ai application config) [localhost]:
OpenSearch port [9200]:
TLS Configuration
TLS certificate setup:
1) Use existing certificate files
2) Generate sa-ai TLS certificate signed by an existing CA
Choose [1-2] [1]:
Option 1 — existing certificates:
Copy/generate certificates into /app/sa-ai/certs? [Y/n]:
HTTPS certificate path (sa-ai) [/path/to/sa-ai-cert.pem]:
HTTPS key path (sa-ai) [/path/to/sa-ai-key.pem]:
OpenSearch CA certificate path [/path/to/ca-cert.pem]:
Option 2 — certificate generation:
Copy/generate certificates into /app/sa-ai/certs? [Y/n]:
Common Name (CN) for sa-ai certificate [hostname.example.com]:
Country for sa-ai certificate [RU]:
State for sa-ai certificate [Moscow]:
Locality for sa-ai certificate [Moscow]:
Organization for sa-ai certificate [Search Anywhere]:
Subject Alternative Names (comma-separated DNS/IP) [127.0.0.1,localhost,hostname]:
CA certificate path (signs sa-ai cert) [/path/to/ca-cert.pem]:
CA private key path (signs sa-ai cert) [/path/to/ca-key.pem]:
5. JDK Selection
Choose Java runtime:
1) OpenSearch JDK [/app/opensearch/jdk/bin/java, version 21]
2) Bundled JDK [jdk-21.0.5-linux-x64.tar.gz -> /app/sa-ai/jdk]
3) System Java [/usr/bin/java, version 21]
4) Custom path [enter path to java binary or JDK home]
Choose [1-4] [1]:
| Option | Description |
|---|---|
opensearch | JDK from OpenSearch installation. Offered only if opensearch.service is detected and JDK is running. |
bundled | JDK from artifacts/source/jdk-*.tar.gz. Extracted to SA_AI_HOME/jdk. |
system | java from PATH. Must be version 21+. |
custom | Path to java binary or JDK directory. |
6. OpenSearch Configuration
Create or update OpenSearch internal user (sa_ai_user) on this host? [Y/n]:
Store sa-ai service password in OpenSearch keystore (_core/keystore)? [Y/n]:
OpenSearch API URL [https://localhost:9200]:
OpenSearch admin user (basic auth) [admin]:
OpenSearch admin password (admin): ← input hidden
Password for OpenSearch internal user (sa_ai_user): ← input hidden, confirmation required
7. Confirmation
The installer prints final parameters:
Local OpenSearch: yes
Home: /app/sa-ai
Logs: /app/logs/sa-ai
Port: 8010
HTTPS enabled: true
Service user: opensearch
JDK source: opensearch
Java binary: /app/opensearch/jdk/bin/java
OpenSearch host: localhost:9200
Cert mode: provided
...
!!! AT THIS POINT WE START TO MAKE CHANGES IN OPERATING SYSTEM !!!
Continue installation? [y/N]:
After entering y, installation proceeds without stops.
8–14. Automatic
Remaining steps are executed automatically:
| Step | What Happens |
|---|---|
| 8. Pre-install preparation | Creating Linux user, directories, setting permissions |
| 9. Backup & prepare | Backup current config to SA_AI_HOME.bak.<timestamp>/, stopping service |
| 10. Deploy files | Copying JAR, JDK, model_registry.json, TLS files |
| 11. Render templates | Generating application.properties and systemd unit file |
| 12. Set ownership | Setting owner for SA_AI_HOME and SA_AI_LOGS |
| 13. OpenSearch user & keystore | API calls to OpenSearch for creating user and keystore |
| 14. Register & start | Registering systemd unit, daemon-reload, enable, start, health-check |
Installation from YAML Configuration
Configuration Preparation
Copy the example and fill in for your environment:
cp example_config_sa-ai.yaml my-config.yaml
Launch
sudo ./setup.sh install -c my-config.yaml
Configuration Example
SaAi:
runtime:
noRoot: n # y — rootless mode
saAiLocation:
OVERWRITE_EXISTING: n # y — allow overwriting existing installation
SA_AI_HOME: /app/sa-ai
SA_AI_LOGS: /app/logs/sa-ai
SA_AI_PORT: 8010
SA_AI_USER: opensearch
jdkSelection:
JDK_SOURCE: opensearch # opensearch | bundled | system | custom
JDK_CUSTOM_PATH: "" # filled only when JDK_SOURCE: custom
prereqConfiguration:
CONFIGURE_USER: y
CONFIGURE_KEYSTORE: y
OS_API_URL: https://localhost:9200
OS_ADMIN_USER: admin
# Passwords not specified — entered interactively
# Needed only when local OpenSearch is absent
openSearchConnection:
HOST: opensearch.example.com
PORT: 9200
CERT_PATH: /app/sa-ai/certs
certManagement:
CERT_MODE: provided # provided | generate
COPY_TO_HOME: y
# Parameters below are relevant only when CERT_MODE: generate
CERT_CN: sa-ai.local
CERT_COUNTRY: RU
CERT_STATE: Moscow
CERT_CITY: Moscow
CERT_ORG: Search Anywhere
CERT_ALT_NAMES: 127.0.0.1,localhost,sa-ai.local
OS_CA_KEY_PATH: "" # path to CA key when CERT_MODE: generate
certFiles:
TLS_CERT_PATH: "" # HTTPS certificate when CERT_MODE: provided
TLS_KEY_PATH: "" # HTTPS key when CERT_MODE: provided
OS_CA_CERT_PATH: "" # OpenSearch CA certificate (always needed)
tls:
SERVER_SSL_ENABLED: y # y — enable HTTPS; n — for debugging only
updateOptions:
UPDATE_SERVICE_FILE: n # y — regenerate systemd unit during update
UPDATE_APPLICATION_PROPERTIES: n # y — regenerate application.properties during update
YAML Parameters Reference
SaAi.runtime
| Parameter | Values | Description |
|---|---|---|
noRoot | y / n | Rootless mode: output privileged commands instead of executing them |
SaAi.saAiLocation
| Parameter | Default | Description |
|---|---|---|
OVERWRITE_EXISTING | n | Allow overwriting existing installation |
SA_AI_HOME | /app/sa-ai | Service installation directory |
SA_AI_LOGS | /app/logs/sa-ai | Logs directory |
SA_AI_PORT | 8010 | Service port |
SA_AI_USER | opensearch | Linux service user. Created automatically if absent. |
SaAi.jdkSelection
| Parameter | Values | Description |
|---|---|---|
JDK_SOURCE | opensearch | JDK from local OpenSearch installation |
bundled | JDK from artifacts/source/jdk-*.tar.gz | |
system | Java from PATH (version 21+) | |
custom | Path specified in JDK_CUSTOM_PATH | |
JDK_CUSTOM_PATH | — | Path to java binary or JDK root directory (when JDK_SOURCE: custom) |
SaAi.prereqConfiguration
Applied only when local OpenSearch is present (opensearch.service).
| Parameter | Default | Description |
|---|---|---|
CONFIGURE_USER | y | Create or update internal user sa_ai_user in OpenSearch Security |
CONFIGURE_KEYSTORE | y | Store service password in keystore (sm.core.ai.password) |
OS_API_URL | https://localhost:9200 | OpenSearch API URL |
OS_ADMIN_USER | admin | OpenSearch administrator (basic auth) |
Passwords (OS_ADMIN_PASSWORD and sa_ai_user password) are not specified in YAML. They are requested by the installer from the keyboard.
SaAi.openSearchConnection
Filled only when OpenSearch is not installed on the host.
| Parameter | Description |
|---|---|
HOST | Remote OpenSearch host |
PORT | Remote OpenSearch port |
CERT_PATH | Directory where TLS materials for connection are placed |
SaAi.certManagement
| Parameter | Values | Description |
|---|---|---|
CERT_MODE | provided | Use existing files from certFiles |
generate | Generate certificate signed by CA from OS_CA_CERT_PATH / OS_CA_KEY_PATH | |
COPY_TO_HOME | y / n | Copy files to SA_AI_HOME/certs. When CERT_MODE: generate — always y |
CERT_CN | hostname | Common Name for generated certificate |
CERT_COUNTRY | RU | Country |
CERT_STATE | Moscow | State |
CERT_CITY | Moscow | Locality |
CERT_ORG | Search Anywhere | Organization |
CERT_ALT_NAMES | hostname, localhost, 127.0.0.1 | Subject Alternative Names (DNS and IP separated by commas) |
OS_CA_KEY_PATH | — | Path to CA private key (only when CERT_MODE: generate) |
SaAi.certFiles
| Parameter | Description |
|---|---|
TLS_CERT_PATH | HTTPS certificate for sa-ai service (when CERT_MODE: provided) |
TLS_KEY_PATH | HTTPS private key (when CERT_MODE: provided) |
OS_CA_CERT_PATH | OpenSearch CA certificate. Always needed: sa-ai verifies OpenSearch TLS with it |
SaAi.tls
| Parameter | Default | Description |
|---|---|---|
SERVER_SSL_ENABLED | y | Enable incoming HTTPS. Disable only for local debugging |
SaAi.updateOptions
| Parameter | Default | Description |
|---|---|---|
UPDATE_SERVICE_FILE | n | Regenerate systemd unit during update command |
UPDATE_APPLICATION_PROPERTIES | n | Regenerate application.properties during update command |
Post-Installation Verification
# Service status
sudo systemctl status sa-ai
# Port listening
sudo ss -tlnp | grep :8010
# Health endpoint
curl -k -s https://localhost:8010/health
# List of available models
curl -k -s -u sa_ai_user:'<PASSWORD>' https://localhost:8010/models
Test LLM request:
curl -k -s -u sa_ai_user:'<PASSWORD>' \
-X POST https://localhost:8010/llm-run \
-H 'Content-Type: application/json' \
-d '{
"model": "qwen-mini",
"mode": "summary",
"rows": [{"message": "test"}],
"input": "message",
"output": "summary",
"systemMessage": "You are concise",
"userMessage": "Return one short sentence",
"maxTokens": 32
}'
Model Registry
File: ${SA_AI_HOME}/configs/model_registry.json
The registry describes available LLMs. The service re-reads it without restart, but restart is allowed:
sudo systemctl restart sa-ai
Example entry:
[
{
"name": "qwen-mini",
"provider": "vllm",
"endpoint": "http://127.0.0.1:11434/v1",
"model_id": "qwen2.5:0.5b",
"type": "chat",
"max_context": 32768,
"default_temperature": 0.2,
"default_max_tokens": 512
}
]
| Field | Description |
|---|---|
name | Model identifier in API |
provider | vllm, ollama or other OpenAI-compatible provider |
endpoint | Provider URL |
model_id | Model identifier on provider side |
type | chat or completion |
max_context | Context window size |
default_temperature | Default temperature |
default_max_tokens | Maximum number of tokens in response |
Application Configuration
File: ${SA_AI_HOME}/application.properties
File is generated by installer from template. Key parameters:
| Field | Description |
|---|---|
server.port | Service port |
server.ssl.enabled | Enable HTTPS |
server.ssl.certificate | Path to SSL certificate |
server.ssl.certificate-private-key | Path to private key |
llm.model-registry-path | Path to model registry |
llm.auth.opensearch-host | OpenSearch host for authentication |
llm.auth.opensearch-port | OpenSearch port |
llm.auth.ca-cert-path | Path to OpenSearch CA certificate |
logging.file.path | Logs directory |
OpenSearch Integration
sa-ai creates in OpenSearch:
- Internal user:
sa_ai_user - Keystore key:
sm.core.ai.password
These names are hardcoded in the application.
Two independent flags are available:
CONFIGURE_USER— creates or updatessa_ai_userCONFIGURE_KEYSTORE— writes password via API:
PUT /_core/keystore/sm.core.ai.password
{ "value": "<SA_AI_USER_PASSWORD>" }
Update
sudo ./setup.sh update
sudo ./setup.sh update -c my-config.yaml
Update steps:
- Stop service
- Replace JAR
- Optionally — regenerate unit file and
application.properties(controlled byupdateOptionsin YAML) - Start service and health-check
Uninstall
sudo ./setup.sh uninstall # interactive
sudo ./setup.sh uninstall -y # no questions; removes unit, home, logs
sudo ./setup.sh uninstall -y --keep-data # no questions; removes only unit
sudo ./setup.sh uninstall -y --remove-user # also removes service user
| Flag | Behavior |
|---|---|
| (no flags) | Interactively offers to delete SA_AI_HOME, SA_AI_LOGS and Linux user |
-y | No questions; removes unit, SA_AI_HOME, SA_AI_LOGS |
-y --keep-data | Removes only systemd unit |
-y --remove-user | Also removes service Linux user |
Rootless Mode
In this mode, the installer does not execute privileged commands on its own. Instead, it outputs blocks of commands that the operator executes manually, then confirms.
./setup.sh install --no-root
After each block of privileged commands, the installer requests:
Execute the commands above, then press Enter to continue...
After confirmation, the installer checks that the expected state has been reached. When SA_AI_USER differs from the current user, the username must be explicitly set via YAML or entered in interactive mode.