Tutorial — Provision a Web Server
This tutorial walks through a complete automation scenario: provisioning a web server with nginx, a config file, and a firewall rule.
Prerequisites
Section titled “Prerequisites”- SysopKit installed in a project or globally
- Access to a target host via SSH
Step 1: Create the Entry Point
Section titled “Step 1: Create the Entry Point”Every SysopKit script starts with main(), which creates a root execution context, runs your workflow, and sets process.exitCode on failure:
import { main } from 'sysopkit/start';
await main(async () => { // Your automation logic here});Step 2: Define the Host
Section titled “Step 2: Define the Host”Define the target host with an SSH connector.
import { SSHConnector } from 'sysopkit/connector/ssh';import { main } from 'sysopkit/start';
await main(async () => { await using c = new SSHConnector({ host: '192.168.1.10', user: 'sysop' });});The await using pattern ensures your SSH connection is cleaned up when the script exits.
Step 3: Apply Operations to Host
Section titled “Step 3: Apply Operations to Host”Use apply() to run operations against one or more hosts:
import { apply } from 'sysopkit';import { SSHConnector } from 'sysopkit/connector/ssh';import { sh } from 'sysopkit/op/sh';import { main } from 'sysopkit/start';
await main(async () => { await using c = new SSHConnector({ host: '192.168.1.10', user: 'sysop' });
await apply('provision web', c, async () => { await sh('hostname'); });});Step 4: Run Commands with Privilege Escalation
Section titled “Step 4: Run Commands with Privilege Escalation”Many system operations require root. Use sudo() middleware to elevate privileges:
import { sudo } from 'sysopkit/middleware/sudo';
await apply('provision web', c, async () => { await sudo(async () => { await sh('apt update'); await sh('apt install -y nginx'); });});Step 5: Manage Files
Section titled “Step 5: Manage Files”Use file operations to create configuration files idempotently:
import { createFile } from 'sysopkit/op/file';
await sudo(async () => { await createFile({ path: '/etc/nginx/sites-available/default', content: `server { listen 80 default_server; root /var/www/html; index index.html;}`, mode: 0o644, user: 'root', group: 'root', });});Step 6: Track Changes
Section titled “Step 6: Track Changes”Use onChange() to react to config changes:
import { onChange, latch } from 'sysopkit';
const configChanged = latch();await onChange(configChanged, async () => { await createFile({ path: '/etc/nginx/sites-available/default', content: NGINX_CONFIG, mode: 0o644, user: 'root', group: 'root', });});
if (configChanged()) { await sh('systemctl reload nginx');}Step 7: Organize with Tasks
Section titled “Step 7: Organize with Tasks”Use task() to group related operations under a named context:
import { task } from 'sysopkit';
await sudo(async () => { await task('install packages', async () => { await sh('apt install -y nginx ufw'); });
await task('configure firewall', async () => { await sh('ufw allow 80/tcp'); await sh('ufw allow 443/tcp'); });
await task('enable services', async () => { await sh('systemctl enable --now nginx'); await sh('systemctl enable --now ufw'); });});Complete Script
Section titled “Complete Script”import { apply, task, onChange, latch } from 'sysopkit';import { SSHConnector } from 'sysopkit/connector/ssh';import { sudo } from 'sysopkit/middleware/sudo';import { createFile } from 'sysopkit/op/file';import { sh } from 'sysopkit/op/sh';import { main } from 'sysopkit/start';
const NGINX_CONFIG = `server { listen 80 default_server; root /var/www/html; index index.html;}`;
await main(async () => { await using c = new SSHConnector({ host: '192.168.1.10', user: 'sysop' });
await apply('provision web', c, async () => { await sudo(async () => { await task('install packages', async () => { await sh('apt update'); await sh('apt install -y nginx ufw'); });
const configChanged = latch(); await onChange(configChanged, async () => { await createFile({ path: '/etc/nginx/sites-available/default', content: NGINX_CONFIG, mode: 0o644, user: 'root', group: 'root', }); });
if (configChanged()) { await task('reload nginx', async () => { await sh('systemctl reload nginx'); }); }
await task('configure firewall', async () => { await sh('ufw --force enable'); await sh('ufw allow 80/tcp'); await sh('ufw allow 22/tcp'); }); }); });});You now have a complete provisioning script: it connects over SSH, installs packages, manages config idempotently, reloads nginx only when the config changes, and configures the firewall. From here, continue with Core Concepts to understand the execution model, or scale out with Inventory to run this script against a fleet of hosts.