Linux Server
Tested on Ubuntu 22.04 / 24.04, Debian 12, Rocky Linux 9. The package is a self-contained x64 binary — no .NET runtime installation required.
Requirements
- Linux x64 (amd64)
unzipto extract the archive- Nginx (recommended) for HTTPS and reverse proxy
- A writable data directory (e.g.,
/var/basis/data)
Step 1 — Create a Dedicated User
Running as a non-root user is strongly recommended:
sudo useradd -r -s /bin/false basis
Step 2 — Extract the Package
sudo mkdir -p /opt/basis
sudo unzip BasisServer-v1.0-linux-x64.zip -d /opt/basis
sudo chmod +x /opt/basis/Basis.Web
sudo chown -R basis:basis /opt/basis
Step 3 — Create the Data Directory
sudo mkdir -p /var/basis/data/businesses
sudo mkdir -p /var/basis/data/attachments
sudo chown -R basis:basis /var/basis
Step 4 — Configure
sudo cp /opt/basis/appsettings.Custom.json.template /opt/basis/appsettings.Custom.json
sudo nano /opt/basis/appsettings.Custom.json
Edit the file with your values:
{
"Urls": "http://+:8080",
"LicenseOptions": {
"Edition": "Server",
"Key": "YOUR-LICENSE-KEY-HERE"
},
"DatabaseOptions": {
"BasePath": "/var/basis/data"
},
"JwtSettings": {
"SecretKey": "CHANGE-THIS-TO-A-STRONG-RANDOM-STRING-MIN-32-CHARS",
"Issuer": "Basis",
"Audience": "BasisUsers"
}
}
Restrict permissions on the config file (it contains secrets):
sudo chown basis:basis /opt/basis/appsettings.Custom.json
sudo chmod 640 /opt/basis/appsettings.Custom.json
Step 5A — Test Run (manual)
Before setting up the service, confirm the app starts cleanly:
cd /opt/basis
sudo -u basis ./Basis.Web
Open a browser and go to http://<server-ip>:8080. Stop with Ctrl+C.
Step 5B — Install as systemd Service (recommended)
sudo cp /opt/basis/basis.service /etc/systemd/system/basis.service
sudo systemctl daemon-reload
sudo systemctl enable basis
sudo systemctl start basis
sudo systemctl status basis
The bundled basis.service file:
[Unit]
Description=Basis Accounting Server
After=network.target
[Service]
Type=notify
WorkingDirectory=/opt/basis
ExecStart=/opt/basis/Basis.Web \
--urls "http://+:8080" \
--DatabaseOptions:BasePath /var/basis/data
Restart=on-failure
RestartSec=10
User=basis
Group=basis
[Install]
WantedBy=multi-user.target
--urls and --DatabaseOptions:BasePath arguments on ExecStart override appsettings.Custom.json. Remove them from ExecStart if you prefer to control everything via the config file.
Service management commands
sudo systemctl start basis # start
sudo systemctl stop basis # stop
sudo systemctl restart basis # restart
sudo systemctl status basis # status
sudo journalctl -u basis -f # follow live logs
sudo journalctl -u basis --since "1 hour ago" # recent logs
Step 6 — Firewall
UFW (Ubuntu / Debian):
sudo ufw allow 8080/tcp
sudo ufw reload
firewalld (Rocky Linux / RHEL / CentOS):
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
Step 7 — Nginx Reverse Proxy + HTTPS
Install Nginx:
sudo apt install nginx # Ubuntu / Debian
sudo dnf install nginx # Rocky / RHEL
Create a site config at /etc/nginx/sites-available/basis:
sudo nano /etc/nginx/sites-available/basis
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Required for Blazor SignalR long-lived connections
proxy_read_timeout 3600;
proxy_send_timeout 3600;
}
}
Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/basis /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Free SSL certificate with Let's Encrypt:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
Updating
sudo systemctl stop basis
# Replace app files — config and data are NOT touched
sudo unzip -o BasisServer-v<new-version>-linux-x64.zip -d /opt/basis
sudo chmod +x /opt/basis/Basis.Web
sudo chown -R basis:basis /opt/basis
sudo systemctl start basis
appsettings.Custom.json and all data in /var/basis/data are never overwritten.
Uninstalling
sudo systemctl stop basis
sudo systemctl disable basis
sudo rm /etc/systemd/system/basis.service
sudo systemctl daemon-reload
sudo rm -rf /opt/basis
# Optional — remove data (irreversible)
sudo rm -rf /var/basis
sudo userdel basis
Windows Server
Supported on Windows Server 2019 / 2022 and Windows 10 / 11 (64-bit). The package includes the .NET runtime — no separate installation needed.
Step 1 — Extract the Package
Extract BasisServer-v1.0-win-x64.zip to a permanent folder, e.g. C:\Basis\. The folder should contain Basis.Web.exe and supporting files.
Step 2 — Configure
Rename the template file:
appsettings.Custom.json.template → appsettings.Custom.json
Edit appsettings.Custom.json with Notepad or any text editor:
{
"Urls": "http://+:8080",
"LicenseOptions": {
"Edition": "Server",
"Key": "YOUR-LICENSE-KEY-HERE"
},
"DatabaseOptions": {
"BasePath": "C:\\BasisData"
},
"JwtSettings": {
"SecretKey": "CHANGE-THIS-TO-A-STRONG-RANDOM-STRING-MIN-32-CHARS",
"Issuer": "Basis",
"Audience": "BasisUsers"
}
}
Create the data folder:
# PowerShell
New-Item -ItemType Directory -Force -Path "C:\BasisData\businesses"
New-Item -ItemType Directory -Force -Path "C:\BasisData\attachments"
Step 3A — Test Run (manual)
cd C:\Basis
.\Basis.Web.exe
Open a browser and go to http://localhost:8080. Stop with Ctrl+C.
Step 3B — Install as Windows Service (recommended)
Run in an Administrator PowerShell:
sc create "BasisServer" `
binPath= "C:\Basis\Basis.Web.exe" `
DisplayName= "Basis Accounting Server" `
start= auto
sc description "BasisServer" "Basis Server Edition — multi-tenant accounting system"
sc start "BasisServer"
appsettings.Custom.json in the app folder. You can also pass them as command-line arguments directly in binPath:binPath= "C:\Basis\Basis.Web.exe --urls http://+:8080 --DatabaseOptions:BasePath C:\BasisData"
Verify the service is running:
sc query "BasisServer"
Service management commands
sc start "BasisServer" # start
sc stop "BasisServer" # stop
sc delete "BasisServer" # uninstall (stop first)
View logs in Event Viewer → Windows Logs → Application → Source: BasisServer.
Step 4 — Firewall
Allow inbound connections on the app port (if accessing from other machines on the network):
# PowerShell (Administrator)
New-NetFirewallRule -DisplayName "Basis Server" `
-Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow
Step 5 — Reverse Proxy (optional, for HTTPS)
Place IIS or Nginx in front of Basis to terminate HTTPS and proxy to http://localhost:8080.
IIS ARR reverse proxy — create a new IIS site and add a web.config:
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxy" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://localhost:8080/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Requires the IIS URL Rewrite and Application Request Routing (ARR) modules.
Updating
- Stop the service:
sc stop "BasisServer" - Replace all files in
C:\Basis\with the new version. Keepappsettings.Custom.json— do not overwrite it. - Start the service:
sc start "BasisServer"
Data in C:\BasisData (or your configured path) is never touched during an update.
Uninstalling
sc stop "BasisServer"
sc delete "BasisServer"
Remove-Item "C:\Basis" -Recurse -Force
# Optional — remove data (irreversible)
Remove-Item "C:\BasisData" -Recurse -Force
Docker
The quickest way to get a server running. The image is a self-contained Cloud edition build.
Quick Start
docker run -d \
--name basis \
-p 8080:8080 \
-v /your/data:/data \
-e JwtSettings__SecretKey="CHANGE-THIS-MIN-32-CHARS" \
-e LicenseOptions__Edition="Cloud" \
ghcr.io/basis-app/basis:latest
Open http://localhost:8080 in a browser.
Docker Compose
Save as docker-compose.yml:
services:
basis:
image: ghcr.io/basis-app/basis:latest
container_name: basis
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./data:/data
environment:
ASPNETCORE_ENVIRONMENT: Production
DatabaseOptions__BasePath: /data
# REQUIRED — change before deploying
JwtSettings__SecretKey: "CHANGE-THIS-TO-A-STRONG-SECRET-AT-LEAST-32-CHARS"
JwtSettings__Issuer: Basis
JwtSettings__Audience: BasisUsers
# Edition & License
LicenseOptions__Edition: Cloud # Cloud | Server
LicenseOptions__Key: "" # Server edition only
docker-compose up -d
docker-compose logs -f basis
/data. If you stop the container without a volume, all business data is lost when the container is removed.
Configuration Reference
All configuration can be set in appsettings.Custom.json (file-based) or via environment variables (replace : with __, e.g., JwtSettings__SecretKey). Environment variables take precedence.
| Key | Required | Notes |
|---|---|---|
| Urls | Required | Listening address and port. Use http://+:8080 for all interfaces. Change port if 8080 is occupied. |
| JwtSettings:SecretKey | Required | Secret used to sign session tokens. Must be at least 32 characters. Use a randomly generated string. Never commit to source control. |
| JwtSettings:Issuer | Optional | JWT issuer claim. Default: Basis. |
| JwtSettings:Audience | Optional | JWT audience claim. Default: BasisUsers. |
| DatabaseOptions:BasePath | Optional | Folder for all SQLite database files. Defaults: Docker → /data, Azure App Service → /home/basis/data, other → {app-dir}/Data. |
| LicenseOptions:Edition | Required | Server or Cloud. Hardcoded to Server in Basis.Web; set to Cloud in Docker image. |
| LicenseOptions:Key | Optional | License key for Server edition. App runs normally without a key; only a renewal banner is shown. |
Related
- Quick Start — first-run setup after installation
- Migration — import data from another accounting system
- Business Settings — company info, features, and export
Basis