Security Overview
Vessl follows a shared-responsibility model: Vessl handles the platform layer (firewall rules, container isolation, credential injection, rate limiting), and you're responsible for your application code and secrets.
Firewall (UFW)
When Vessl provisions your server, it configures UFW (Uncomplicated Firewall) automatically with the following rules:
| Port | Protocol | Open to |
|---|---|---|
| 22 | TCP | Anywhere (SSH) |
| 80 | TCP | Anywhere (HTTP) |
| 443 | TCP | Anywhere (HTTPS) |
| All others | — | Blocked |
Your app doesn't need to open any additional ports. Traefik handles HTTP/HTTPS routing on ports 80 and 443; your container's internal port is never exposed directly to the internet.
Vessl manages firewall state via SSH on provisioning and when syncing server settings. Manual changes may be overwritten. If you need to open a custom port for a specific use case, do it carefully and document it — Vessl won't track or restore it.
Container isolation
Every app and add-on runs in its own Docker container. Containers are isolated at the network level — an app container can reach its own add-ons (database, Redis) via Docker's internal network, but cannot reach another team's containers.
Database add-ons are bound to 127.0.0.1 on the host (not exposed publicly). The only way to connect from outside the server is via SSH tunnel. See Databases & add-ons for details.
SSH keys
Vessl needs SSH access to your server to provision it, sync cron jobs, and run deployment commands. The key Vessl uses is generated per-server and stored encrypted — it's separate from any key you use to SSH in yourself.
You can provide your SSH key to Vessl in three ways when adding a server:
- Generate a new key pair — Vessl creates a key, you add the public key to
~/.ssh/authorized_keyson your server - Use an existing public key — paste your own public key and Vessl uses it
- Password authentication — Vessl connects with a password (less recommended for production)
After provisioning, Vessl only uses SSH for management operations — it does not maintain a persistent connection to your server.
Secrets and environment variables
Environment variables you set in the Variables tab are:
- Stored encrypted at rest in Vessl's database
- Injected into the container at runtime (not baked into the image)
- Never logged or exposed in deploy output
Never commit .env files with real credentials. Use the Variables tab to set secrets, and reference them in your app via process.env.MY_SECRET, os.environ['MY_SECRET'], etc.
Build-time environment is separate from runtime environment. Variables in your Variables tab are not available during the Docker build step unless you explicitly add them as Build Args in app Settings. This prevents secrets from being baked into the image layer cache.
Rate limiting
The Vessl API and dashboard have rate limits in place to prevent abuse. Application-level rate limiting for your own app (e.g. login endpoints, API routes) is your responsibility — Vessl doesn't add rate limiting to your app's traffic.
Two-factor authentication
Enable 2FA on your Vessl account to protect against unauthorized access. See Two-factor authentication for setup instructions.
Dependency vulnerabilities
Vessl scans its own platform dependencies regularly. Your application's dependencies are your responsibility — run npm audit, pip-audit, composer audit, or equivalent tools in your CI pipeline before deploying.
What Vessl doesn't protect
- Your application code — SQL injection, XSS, CSRF, and other OWASP vulnerabilities in your app are outside Vessl's scope
- Secrets in your repository — if you commit a
.envfile to GitHub, Vessl can't prevent that exposure - Server-level changes you make — if you SSH into the server and install packages or change configuration, Vessl doesn't audit or restrict that