This guide takes an initialized oCMS site from the local quick start to a Linux server. It uses one application process, a local SQLite database, systemd and an HTTPS reverse proxy. No Go or Node.js installation is required to run a prebuilt binary.
Start with a build that includes the current security fixes. Review its release notes and verify its archive checksum before running it. Keep the working binary with your site's backup.
Jump to site files, production settings, the service, HTTPS, or verification.
1. Put the application and site files in separate places
Complete the local quick start first. Set a unique administrator password and remove sample accounts you do not need while the site is still private. Under Admin → Modules, deactivate Developer before switching a development database to production; an active Developer module prevents production startup. Stop the local process before copying its database and files.
Review the other active modules too. Deactivate one-off import tools such as Migrator after completing the import, and deactivate integrations you do not use. For example, an active Embed module requires its production proxy token and upstream-host policy; configure those before switching environments, or deactivate Embed if the site does not use it.
The production defaults also require a configured hCaptcha verifier. Before switching environments, open Admin → hCaptcha, enable protection with valid site and secret keys for your domain, and add a captcha field to every active public form. Deactivate unused sample forms under Admin → Forms. Verify a real form submission after deployment; entering keys alone does not test the external verifier.
The examples below use a dedicated Linux user and group named ocms, this binary path, and this initialized site directory:
/opt/ocms/ocms
/srv/ocms/my-site/
.env
data/ocms.db
uploads/
custom/
Create the service account before starting the service. Keep the binary executable and owned by the server administrator. Give ocms access to its site directory and write access to the database directory, uploads and custom files it manages. Protect .env so only the service user and server administrator can read it.
SQLite needs write access to the directory containing the database for its WAL and shared-memory files. One binary can run several independent sites as separate processes, each with its own directory, configuration and port.
2. Adjust the generated configuration
Edit the existing /srv/ocms/my-site/.env. Preserve the generated OCMS_SESSION_SECRET and OCMS_MFA_KEY; do not replace the entire file with this partial example. Change the public URL to your real HTTPS domain:
OCMS_ENV=production
OCMS_DO_SEED=false
OCMS_SITE_URL=https://cms.example.com
OCMS_SERVER_HOST=127.0.0.1
OCMS_SERVER_PORT=8080
OCMS_TRUSTED_PROXIES=127.0.0.1/32
OCMS_API_ALLOWED_CIDRS=127.0.0.1/32
OCMS_REQUIRE_MFA=true
This example keeps the application on loopback and trusts a reverse proxy on the same server. Its API source policy permits protected API-key requests only from loopback clients. Add the actual address ranges of your automation clients when you need remote protected API access; the API cookbook explains scopes, expiry and per-key source restrictions.
Keep production safeguards enabled. Configure the additional allowlists and secrets required by any integrations you activate, such as webhooks or embed providers. Read the first startup error if a required setting is missing. Set up account security before opening the administration interface to routine use.
3. Run the application as a service
For a systemd server, save this example as /etc/systemd/system/ocms-site.service. Adjust its user and paths to match the installation. oCMS reads the site's .env from the working directory.
[Unit]
Description=oCMS site
After=network.target
[Service]
Type=exec
User=ocms
Group=ocms
WorkingDirectory=/srv/ocms/my-site
ExecStart=/opt/ocms/ocms serve
Restart=on-failure
RestartSec=3
TimeoutStopSec=30
UMask=0077
[Install]
WantedBy=multi-user.target
Load the unit, start it, and inspect its status and logs:
sudo systemctl daemon-reload
sudo systemctl enable --now ocms-site
sudo systemctl status ocms-site --no-pager
sudo journalctl -u ocms-site -n 50 --no-pager
A process starting successfully does not prove the site is ready. Check the health endpoint below and confirm that the service stays running. Correct a configuration or permission error before restarting again.
4. Put HTTPS in front of the application
Configure your domain, certificate and HTTP-to-HTTPS redirect at the reverse proxy. Keep port 8080 private. With nginx terminating HTTPS on the same host, put this location block inside the domain's HTTPS server configuration:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
}
This is a single-proxy example: nginx supplies the visitor address instead of retaining a client-supplied forwarding chain. If you add a CDN or another proxy, configure the real proxy addresses and trusted-header handling for that topology.
Validate nginx configuration before reloading it:
sudo nginx -t
sudo systemctl reload nginx
Never expose .env or the database through a separate static-file location. Proxy the application routes to oCMS.
5. Verify the running site
Check both the application on the server and the public HTTPS route. Replace the example domain with your own:
curl --fail --silent --show-error http://127.0.0.1:8080/health
curl --fail --silent --show-error https://cms.example.com/health
Confirm that both return HTTP 200, then load a known page and an uploaded image. Sign in with the intended administrator account and complete two-factor authentication. Check that assets load over HTTPS, and verify the public site again after restarting the service.
6. Keep updates recoverable
Follow the backup and restore walkthrough before replacing a binary. Keep a consistent database snapshot, uploads, custom files, the original configuration and its keys, and the matching old binary. Rehearse a restore in an isolated location.
Test the chosen update with a copied database and your custom theme first. Compiled custom modules must be included in the replacement binary; copying their source beside a prebuilt executable does not load them. Stop the affected service for the replacement, preserve its previous binary and data, then repeat health, content, image and sign-in checks. Follow the release's upgrade instructions before attempting any database rollback.
Continue with the theme gallery and first-site walkthrough. For other server arrangements, see the deployment reference, nginx proxy directives and systemd service reference.