From Local to Production
A Vix application should move from local development to production without becoming a different project.
Local development should be simple:
vix devProduction should be repeatable:
vix deployThe app stays the same.
The environment changes.
The workflow becomes stricter.
The model
The path from local to production is:
local app
-> local build
-> local checks
-> production config
-> service
-> proxy
-> health checks
-> logs
-> deployA serious app should not depend on random manual steps.
The production path must be written down in config and commands.
Local is for feedback
Local development is about speed.
The normal local loop:
vix devThis gives:
watch files
rebuild when needed
restart app
show runtime outputFor one manual run:
vix runFor one file:
vix run main.cppLocal work should be fast, but not careless.
You still need checks before production.
Production is for repeatability
Production is not a terminal left open with:
vix runProduction should be controlled by:
systemd service
Nginx proxy
environment file
health checks
logs
deploy workflowVix provides commands for these pieces:
vix service
vix proxy nginx
vix health
vix logs
vix deployThe goal is not to hide production.
The goal is to make it repeatable.
The production contract
A production-ready Vix app should answer:
How is it built?
How is it started?
Which user runs it?
Which env file is used?
Which port does it listen on?
Which domain exposes it?
How is HTTPS configured?
How is health checked?
How are logs read?
What happens when deploy fails?If the project cannot answer these questions, production is not ready.
Build before production
Before production, build clearly.
vix build --preset releaseThen validate:
vix check --testsA good release flow:
vix fmt --check
vix build --preset release
vix check --testsDo not deploy a project that cannot pass its own checks.
Dependencies before production
After cloning on a server:
vix installThis installs exact locked dependencies from vix.lock.
Do not use update for deployment setup.
vix install = reproduce locked dependency state
vix update = change dependency stateProduction should be reproducible.
That starts with the lockfile.
Environment files
Local apps often use:
.env
.env.exampleProduction apps should also define required variables:
production.env.requiredExample .env.example:
APP_ENV=development
SERVER_HOST=127.0.0.1
SERVER_PORT=8080
SERVER_TLS_ENABLED=false
VIX_LOG_LEVEL=info
VIX_LOG_FORMAT=kv
DATABASE_ENGINE=sqlite
DATABASE_DEFAULT_NAME=./data/app.db
JWT_SECRET=change-me
SESSION_SECRET=change-meExample production.env.required:
APP_ENV
SERVER_HOST
SERVER_PORT
VIX_LOG_LEVEL
DATABASE_ENGINE
JWT_SECRET
SESSION_SECRETCheck local env:
vix env checkCheck production env:
vix env check --productionShow values only when needed:
vix env check --production --show-valuesSecrets are always masked.
Production config in vix.json
Production should be configured in vix.json.
The structure can include:
production.service
production.proxy
production.health
production.logs
production.deployExample:
{
"production": {
"service": {
"name": "api",
"user": "vix",
"working_dir": "/home/vix/apps/api",
"command": "vix run",
"env_file": "/home/vix/apps/api/.env"
},
"proxy": {
"domain": "api.example.com",
"http_port": 8080,
"websocket_enabled": true,
"websocket_port": 9090,
"websocket_path": "/ws",
"tls": {
"enabled": true,
"certificate": "/etc/letsencrypt/live/api.example.com/fullchain.pem",
"certificate_key": "/etc/letsencrypt/live/api.example.com/privkey.pem"
}
},
"health": {
"service": "api",
"local": "http://127.0.0.1:8080/health",
"public": "https://api.example.com/health",
"websocket": "wss://api.example.com/ws"
},
"logs": {
"service": "api",
"nginx_access": "/var/log/nginx/api.access.log",
"nginx_error": "/var/log/nginx/api.error.log"
}
}
}The values must match the server.
Do not put fake production config in a real deploy.
Service layer
The service layer runs the app as a system service.
Use:
vix service initThen:
vix service status
vix service restart
vix service stopThe service should know:
service name
working directory
command
user
environment file
restart policyThe app should not require someone to start it manually after reboot.
Why systemd matters
On Linux servers, systemd gives:
startup on boot
restart on failure
status inspection
journal logs
controlled stop and restartA backend app should run as a managed service.
That gives a clean operational model:
code changes through deploy
runtime through service
logs through journal
public traffic through proxyProxy layer
The proxy layer exposes the app publicly.
For Nginx:
vix proxy nginx initCheck config:
vix proxy nginx checkReload:
vix proxy nginx reloadIssue or renew certificate:
vix proxy nginx certbotThe proxy should map public traffic to the local app.
Example:
https://api.example.com
-> http://127.0.0.1:8080For WebSocket:
wss://api.example.com/ws
-> ws://127.0.0.1:9090TLS model
Production traffic should use HTTPS.
The TLS model:
port 80 redirects to 443
port 443 uses certificate and key
Nginx forwards traffic to local appThe app can stay local:
127.0.0.1:8080Nginx handles public TLS.
This keeps the app simple and the public boundary clear.
WebSocket proxying
If the app uses WebSocket, production config must say it.
Important values:
websocket enabled
public WebSocket URL
local WebSocket host
local WebSocket port
WebSocket path
timeout
heartbeatCheck WebSocket:
vix ws checkOr through health:
vix health websocketWebSocket should not be assumed healthy just because HTTP works.
Health checks
Health checks prove the app is usable.
Run all checks:
vix healthLocal endpoint:
vix health localPublic endpoint:
vix health publicWebSocket endpoint:
vix health websocketA health check should report:
target
URL
expected status
actual status
response time
max response time
healthy yes or no
error when presentProduction without health checks is blind.
The health route
A backend should expose:
GET /healthExample response:
{
"ok": true,
"service": "api",
"status": "healthy"
}This route should be fast.
It should not depend on heavy work.
If it checks database connectivity, make that intentional.
Logs
Production logs must be easy to read.
Use:
vix logsApplication logs:
vix logs appProxy logs:
vix logs proxyError logs:
vix logs errorsFollow logs:
vix logs app --followRead recent logs:
vix logs errors --lines 100Logs should help answer:
Did the service start?
Did it crash?
Did Nginx route traffic?
Did health checks fail?
Which error happened first?Deploy workflow
Deployment should be a sequence of clear steps.
Run:
vix deployPreview:
vix deploy --dry-runVerbose:
vix deploy --verboseSkip pull:
vix deploy --no-pullSkip tests:
vix deploy --no-testsA deployment can include:
git pull
build
tests
service restart
local health check
public health check
proxy check
proxy reload
logs on failure
rollbackThis is the production workflow in one command.
Deploy config
Example:
{
"production": {
"deploy": {
"pull": true,
"branch": "main",
"build": "vix build --preset release",
"tests": true,
"test_command": "vix check --tests",
"service": "api",
"health_local": true,
"health_public": true,
"proxy_check": true,
"proxy_reload": true,
"logs_on_failure": true,
"log_lines": 120,
"rollback": true
}
}
}Each step should be visible.
Deployment should not feel like a black box.
Dry run first
Before trusting production deploy:
vix deploy --dry-runA dry run should show the planned steps without executing them.
Use it when:
setting up a new server
changing service config
changing proxy config
changing deploy config
debugging production flowDry run is not just for beginners.
It prevents avoidable mistakes.
Rollback
Rollback should exist because deploys can fail.
A deploy can fail because:
build failed
tests failed
service restart failed
health check failed
proxy config failed
public endpoint failedIf rollback is enabled, Vix should attempt to restore the previous working state.
The model:
deploy new version
-> check health
-> if failed, restore previous stateRollback should be explicit in config.
Logs on failure
When deployment fails, the next step is logs.
Config:
{
"production": {
"deploy": {
"logs_on_failure": true,
"log_lines": 120
}
}
}This lets deploy show useful context immediately.
A failed deploy without logs forces manual guessing.
Local checks before deploy
Before deployment:
vix fmt --check
vix build --preset release
vix check --tests
vix env check --productionThen:
vix deploy --dry-run
vix deployThis sequence is simple and serious.
Server setup order
For a fresh server, use this order:
vix doctor
vix install
vix env check --production
vix build --preset release
vix service init
vix proxy nginx init
vix health
vix deploy --dry-run
vix deployThis order prevents confusion.
Environment before service.
Service before proxy health.
Proxy before public health.
Deploy after the pieces are ready.
vix doctor in production
Use:
vix doctorWith online check:
vix doctor --onlineDoctor checks the environment.
Use it when:
tools are missing
Vix behaves strangely
a new server was prepared
upgrade may be needed
build or run fails unexpectedlyvix doctor diagnoses.
It does not fix everything automatically.
vix info in production
Use:
vix infoThis shows:
Vix version
Vix root
registry path
store path
global packages
artifact cache
disk usage
local stateUse it before cleanup.
Use it when dependency state is unclear.
Use it when registry or store paths matter.
Registry in production
If production needs project dependencies:
vix registry sync
vix installIf the registry is already synced but outdated:
vix registry syncIf install fails because a package is not found:
vix registry sync
vix installThe registry index is metadata.
The store is package content.
The project lockfile chooses exact versions.
Store cleanup in production
Check store path:
vix store pathPreview cleanup:
vix store gc --project --dry-runRun cleanup:
vix store gc --projectBe careful.
Project-scoped GC can remove cached packages not referenced by the current project lockfile.
Always preview first.
Production database
For SQLite apps:
vix db status
vix db migrate
vix db backupBefore dangerous deploys:
vix db backupFor migration workflows:
vix orm status --db api --dir ./migrations
vix orm migrate --db api --dir ./migrationsDatabase state is production state.
Treat it carefully.
Backups
A production app that writes data needs backups.
For SQLite:
vix db backupThis can copy:
database file
WAL file when present
SHM file when presentBackups are not optional for real systems.
If the app stores important data, backup must be part of the operational model.
Static files
If the app serves static files, the production model should define where they live.
Example:
public/
assets/
uploads/Generated build artifacts should not be mixed with user uploads.
A simple separation:
public = versioned static files
uploads = runtime user files
dist = packaging output
build = build outputThis prevents accidental deletion during cleanup.
Runtime arguments in production
Local runtime arguments can be passed with:
vix run -- --port 8080In production, prefer config and env files.
The service should define the command clearly.
Example:
vix run -- --port 8080But do not hide important values in random shell history.
Put stable production values in the service config or environment file.
Production user
Do not run production apps as root unless there is a real reason.
A production service should define:
user = vix
working directory = /home/vix/apps/apiThe service user should own only what it needs.
This limits damage when something goes wrong.
Ports
A simple production model:
app HTTP port: 8080
app WebSocket port: 9090
Nginx public HTTP: 80
Nginx public HTTPS: 443The app listens locally.
Nginx exposes public traffic.
This keeps the public surface small.
Firewall model
The public server should expose only what is needed.
Usually:
22 for SSH
80 for HTTP redirect
443 for HTTPSApplication ports like 8080 and 9090 should normally stay local.
This depends on your deployment setup, but the default should be conservative.
Production output
Production commands should print useful summaries.
For deploy:
App
Branch
Pull yes or no
Build command
Tests enabled or disabled
Service name
Health checks
Proxy check
Logs on failure
Rollback
Dry run
VerboseFor proxy init:
App
Domain
HTTP upstream
WebSocket upstream
TLS enabled or disabled
Site file
Enabled path
Certificate pathsFor health:
Target
URL
Expected status
Actual status
Time
Healthy yes or noThis is how production stays explainable.
Local to production example
A full backend flow:
vix new api --template backend
cd api
vix install
vix devBefore commit:
vix fmt --check
vix build
vix check --testsBefore release:
vix build --preset release
vix check --testsOn server:
vix doctor
vix install
vix env check --production
vix build --preset release
vix service init
vix proxy nginx init
vix healthDeploy:
vix deploy --dry-run
vix deployInspect if something fails:
vix logs errors --lines 120
vix service status
vix healthCommon mistakes
Running production manually
Wrong:
ssh server
vix runCorrect:
vix service init
vix service restart
vix healthUpdating dependencies during deploy setup
Wrong:
vix updateCorrect:
vix installSkipping env checks
Wrong:
vix deployBetter:
vix env check --production
vix deployChecking only local health
Wrong:
vix health localBetter:
vix health local
vix health publicIf WebSocket is used:
vix health websocketReloading proxy without checking config
Wrong:
sudo systemctl reload nginxBetter:
vix proxy nginx check
vix proxy nginx reloadCleaning store without preview
Wrong:
vix store gc --projectCorrect:
vix store gc --project --dry-run
vix store gc --projectProduction checklist
Before first deploy:
Vix installed
vix doctor passes
dependencies installed with vix install
.env exists on server
production.env.required is satisfied
release build passes
tests pass
systemd service exists
Nginx config exists
TLS certificate exists if TLS is enabled
local health passes
public health passes
logs are readable
deploy dry run looks correctAfter deploy:
service is active
local health is healthy
public health is healthy
WebSocket health is healthy when enabled
logs do not show startup errors
rollback path is knownWhat you should remember
Local development is for fast feedback.
Production is for repeatable operation.
The model:
vix dev
-> vix build
-> vix check
-> vix service
-> vix proxy
-> vix health
-> vix logs
-> vix deployDo not treat production as a different universe.
Use the same project.
Make the environment explicit.
Make the service repeatable.
Make the proxy visible.
Make health checks mandatory.
Make logs easy to read.
The core rule:
local should be fast
production should be repeatable
both should be understandable