Deployment
An IWC application builds into one self-contained bundle and runs on the same iwc binary you develop with. One command, iwc deploy, puts it on cPanel hosting or on your own server. This page is the production workflow end to end; Tutorial 16 walks through it with a real app.
The workflow
iwc validate # files + tests + production build + security audit
iwc deploy prod # build, upload, check the live siteThe first iwc deploy prod asks where the site will run and sets it up; every later run just deploys. prod is any name — staging, client-a — and each target is set up and saved separately.
Deploy with iwc deploy
IWC deploy Setting up deploy target "prod" for shop.
Where will the site run?
1) Shared hosting with cPanel — most web hosting plans; no SSH needed
2) My own server or VPS (SSH) — IWC installs the service, web server and HTTPS
Choose [1]:The wizard asks only for what it doesn't know, says where to find each detail, and writes the target into package.iwc itself. Logins never go into the project.
Shared hosting with cPanel
You need your cPanel address, your cPanel username and an API token (cPanel → Security → Manage API Tokens → Create). IWC then lists the account's domains to choose from — or creates a subdomain or folder — and deploys through cPanel's own API. No SSH, nothing to install.
→ deploying 1.2.0 3f9c2a7e1b04c5d6 to public_html
• uploading the package (9427 KB)
• extracting r20261109141516-3f9c2a7e1b04c5d6 into public_html
✓ release 1.2.0 3f9c2a7e1b04c5d6 is in place (database and settings.env kept)
✓ https://example.com answers 200 and the app's health check is ok- The site runs as a CGI program: pages, sign-in, APIs, uploads and jobs all work. The database (SQLite in
data/, or PostgreSQL throughsettings.env) is created on the first visit. - Every deploy keeps
data/andsettings.env; a folder that already holds another site is listed first and its.htaccesskept as.htaccess.before-iwc. - The address and user are saved in
~/.iwc/deploy, the token encrypted in IWC's vault — on your computer only.
Your own server or VPS
Any Linux server with SSH and systemd. You give its address, SSH user and port; IWC shows the server's host-key fingerprint for you to compare before trusting it, installs your SSH key with one password login (typed into ssh's own prompt), and sets the server up on the first deploy:
| Service | iwc-<package>-<target> under systemd: starts at boot, restarts on failure, unprivileged, listening on 127.0.0.1 |
| Web server and HTTPS | Added beside an existing nginx or Caddy (certificates through certbot or Caddy), or Caddy installed on a server with neither; configuration checked before it's loaded |
| Releases | Activated atomically, only if their migrations succeed; the previous one keeps serving until then |
Setting the server up needs root or passwordless sudo; deploying afterwards doesn't. Full details: Your own server.
After deploying
iwc server status prod # live release, stored releases, the public URL checked from here
iwc logs prod # the live app's log
iwc rollback prod # put the previous release back (or: iwc rollback prod <id>)
iwc deploy prod # ship the next changeRollback works on both kinds of target and keeps the database: servers keep the last 10 releases, cPanel hosting the last 5 (in ~/.iwc-releases, outside every web folder).
Without a terminal (CI)
IWC_CPANEL_TOKEN=… iwc deploy prod --host example.com --user acct # cPanel
iwc deploy prod --host deploy@203.0.113.10 --key ci_key --fingerprint SHA256:… # own serverWhat a build contains
| File | Contents |
|---|---|
dist/manifest.json | The compiled project, including your stylesheets |
dist/assets/ | Your static files |
dist/openapi.json, openapi.yaml | OpenAPI 3.1 for every API |
dist/migrations/sqlite.sql, postgres.sql | The schema as SQL, for review |
dist/release.json | Name, version, content hash, build time |
dist/report.json | What each route sends, against its size budget |
Other ways to run it
iwc deploy covers most sites. These remain for setups it doesn't:
| Command | Best for | |
|---|---|---|
| Run it yourself | iwc start dist behind your own proxy | Containers, platforms with their own process manager |
| Your own release agent | iwc host on the server, a url target in package.iwc | Infrastructure you supervise yourself |
| Upload by hand | iwc build --zip, upload and extract in File Manager | Hosting without cPanel's API |
Run it yourself
scp -r dist server:/srv/app/
ssh server 'cd /srv/app && APP_DATABASE_URL=postgres://… iwc start dist --host 127.0.0.1 --port 8080 --trust-proxy loopback'iwc start serves in production mode: no seed rows, simulated services refused, Secure cookies and HSTS. Behind a proxy, --trust-proxy makes rate limits and logs see each visitor rather than the proxy. --base /shop serves the app under a path prefix. Health check: GET /_iwc/health.
Your own release agent
# server
IWC_DEPLOY_TOKEN=<32+ random characters> iwc host --dir /srv/iwc-host --port 8080 --trust-proxy loopback{ "name": "shop", "version": "1.4.0", "iwc": 1, "deploy": { "prod": { "url": "https://deploy.example.com", "tokenEnv": "IWC_DEPLOY_TOKEN" } } }iwc deploy prod, iwc logs prod, iwc rollback prod and iwc pull prod copy/ then work against it over HTTPS with the token.
Upload a zip by hand
iwc build --zip --folder shop # → shop.zip beside the projectUpload and extract in public_html, then open https://yourdomain.com/shop/. Background jobs run briefly after each response; add a cron job running iwc.cgi run-jobs for scheduled work. --arch arm64 targets ARM hosts. Full details: Shared hosting.
Production configuration
| Setting | How |
|---|---|
| Database | app.iwc database.url: {"env": "NAME"}; set NAME to a postgres:// URL, or sqlite |
| Secrets | Environment variables only. iwc secrets export writes development vault values for a server; .env files are never packaged |
| Encryption key | IWC_ENCRYPTION_KEY for encrypted fields; iwc migrate --reencrypt rotates it |
| Site address | app.iwc seo.siteUrl for canonical links, sitemaps and absolute URLs |
| Secrets on your own server | iwc server env prod set KEY (asked without echo; restarts the app). On cPanel: settings.env in the site's folder |
| Plain HTTP for testing | --secure=false or IWC_INSECURE=1 — never on the internet |
Migrations in production
Every start plans migrations from the schemas. New tables, fields and indexes apply automatically; removed fields are left in place so data is kept; type changes and new required fields without defaults on non-empty tables are refused with an explanation. IWC refuses a database holding tables it did not create (IWC_ADOPT_DATABASE=1 to adopt one deliberately). On your own server, a release whose migrations fail is never activated. On cPanel hosting, migrations run on the first visit after a deploy; iwc rollback puts the previous code back.
Backups
iwc backup dist --out /var/backups/app-$(date +%F).iwcbak.tar.gz --keep 14
iwc restore /var/backups/app-2026-11-09.iwcbak.tar.gz dist # into an empty database
iwc restore /var/backups/app-2026-11-09.iwcbak.tar.gz dist --replace # over current dataA backup is one .tar.gz with every table, the audit trail and uploaded files. Restores run in one transaction. Backups move between SQLite and PostgreSQL in either direction.
Native apps
iwc build --android --release # signed APK + AAB when IWC_ANDROID_KEYSTORE… are set
iwc build --ios --release # device archive; App Store .ipa when platforms.ios.teamId is setSee Android & iOS apps.
Hosting on ONIWC
ONIWC's own hosting runs websites made with the Website Generator today. Deploying your own IWC applications to ONIWC-managed hosting is not available yet; the options above work on any server now.
Source: apps/marketing/content/docs/deployment.md