What Shippers is, how to deploy a WebforJ or Spring app, and how to drive it all from the shippers CLI — including wiring shippers deploy into CI/CD.
Shippers is one-click hosting for untrusted, third-party builds — you upload, Shippers runs it in an isolated container and hands back a live URL. See how it works for the full deploy pipeline and pricing for trial/plan details.
WebforJ (.war or an executable Spring Boot .jar, auto-detected) and Spring (.jar) are the two supported application frameworks today, both deployed from a single upload. React/Flutter/Angular/Next.js (static export)/plain HTML deploy the same way, as static builds.
Create an app, drag in your build, and get a live URL at yourapp.shippers.cloud — no server setup, no Dockerfile to write.
Every deployed app runs in its own hardened container with hard CPU/memory/pid limits and no host networking, since Shippers hosts strangers' untrusted uploads.
Every account starts on a 7-day free trial (no card required) with a 1-app cap. Pro lifts that to 3 apps, 3 managed databases, and CLI access.
Provision an internal-only PostgreSQL or MySQL instance and wire its connection details into your app's environment variables — no separate hosting to manage. See connecting to a database below.
Set per-app env vars from the dashboard or via shippers env push. Changes take effect on the next deploy.

WebforJ apps can be uploaded as either a .war (run in Tomcat) or an executable Spring Boot .jar (webforJ's Spring Boot integration) — Shippers detects which one you've uploaded from the file itself, so nothing needs to be configured up front. The JDK version is also auto-selected to match whatever your project was compiled against.
mvn package for a traditional WAR, or your Spring Boot build (mvn package / gradle bootJar) if you're using webforJ's Spring Boot starter..war or .jar file. Shippers deploys it at the app's root path (/) and hands back a live yourapp.shippers.cloud URL once it's up.A Spring app is always uploaded as an executable Spring Boot .jar — the same fat-jar produced by spring-boot-maven-plugin's repackage goal or Gradle's bootJar task. Shippers runs it directly with java -jar, choosing the JDK image that matches your compiled bytecode.
mvn package or gradle bootJar — make sure the Spring Boot Maven/Gradle plugin's repackage step actually runs, since a plain, non-repackaged jar won't be accepted..jar. Shippers force-binds the container to port 8080 regardless of your own server.port, so SERVER_PORT is a reserved environment variable name — set your app's other config as regular env vars instead.yourapp.shippers.cloud.
A database is provisioned independently of any one app, from the database step of "Create a deployment" or the Databases section of your dashboard. It's internal-only — reachable from your own Shippers apps over the private network, never from the public internet — so there's no external client access (no DBeaver, no migrations run from your laptop). Its detail page shows five separate connection fields — Host, Port, Database, Username, and a reveal-to-view Password — rather than one connection string, and you wire those into your app through environment variables, not by baking them into your build.
shippers env push.PORT (Node.js) and SERVER_PORT (Spring Boot's server.port) are reserved — nothing database-related is, so name your own keys however your app expects them.Wiring depends on which artifact you're deploying (see deploying a WebforJ app above) — only the Spring Boot path gets automatic wiring.
Spring Boot .jar (webforJ's Spring Boot integration)
SPRING_DATASOURCE_URL (built as jdbc:postgresql://<host>:<port>/<database> or the mysql equivalent), SPRING_DATASOURCE_USERNAME, and SPRING_DATASOURCE_PASSWORD.pom.xml/build.gradle has org.postgresql:postgresql or com.mysql:mysql-connector-j on the classpath, alongside spring-boot-starter-data-jpa (or -jdbc). Spring Boot's relaxed-binding datasource auto-configuration reads those three env vars with no datasource bean of your own.shippers deploy.Plain .war (Tomcat, no Spring Boot)
Tomcat does no property binding of its own, so nothing wires a datasource automatically — you read the connection details back yourself.
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD. ("Link a database" still works here too — it just prefills the Spring-named keys as plain, harmless env vars; a WAR won't read them on its own either way.)System.getenv(...) and build a JDBC connection yourself, e.g. via HikariCP:HikariConfig config = new HikariConfig();
config.setJdbcUrl(
"jdbc:postgresql://" + System.getenv("DB_HOST") + ":" +
System.getenv("DB_PORT") + "/" + System.getenv("DB_NAME"));
config.setUsername(System.getenv("DB_USER"));
config.setPassword(System.getenv("DB_PASSWORD"));
DataSource dataSource = new HikariDataSource(config);
A Spring app is always an executable Spring Boot jar, so the wiring is always the automatic path — identical to webforJ's Spring Boot case above.
SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, and SPRING_DATASOURCE_PASSWORD for you, built from the database's own Host/Port/Database/Username/decrypted Password.org.postgresql:postgresql or com.mysql:mysql-connector-j, alongside spring-boot-starter-data-jpa (or -jdbc). No datasource bean of your own is required — Spring Boot's relaxed binding auto-configures one from the env vars directly.8080 via the reserved SERVER_PORT key — don't reuse that name for anything else, including a second datasource.Deploy from your own machine or CI instead of dragging a file into the browser.
The CLI is a Pro-only feature — a trialing account can still deploy from the dashboard, but can't create or use a CLI token. Create one from your profile's "CLI access" panel after upgrading.
npm install -g @shippers/clishippers login [token]Authenticate with a personal access token (created from the dashboard). Omit the argument to be prompted; pass it directly for non-interactive use, e.g. in CI.
shippers initLink the current directory to a shippers app — creates one or links an existing one, then writes a .shippers.json file recording the app ID. Interactive only.
shippers deploy [file]Deploy the linked app. With no argument, zips the current directory (excluding .git/node_modules, respecting .shippersignore/.gitignore); or pass a prebuilt .war/.jar directly. Streams status until the deploy finishes or fails.
shippers env pull [file]Write the linked app's environment variables to a local file (default .env).
shippers env push [file]Upload environment variables from a local file — a full replace, not a merge. Redeploy for the new values to take effect.
shippers appsList every app in your account and its current deployment status.
shippers deploy works the same in CI as it does on your laptop — the only difference is authenticating without a terminal to type into.
SHIPPERS_TOKEN in a GitHub Actions repo secret). The raw token is shown once — copy it immediately. Give it a name that identifies the pipeline using it, so you can revoke just that one later without affecting others.shippers init on your own machine to create or link the app, then commit the .shippers.json file it writes to your repo. init is interactive (it asks which app to link), so it isn't something CI can run — but once .shippers.json exists in the repo, shippers deploy needs no further linking step.shippers login accepts the token as an argument instead of prompting, so a CI step can call it directly with the secret from step 1:shippers login "$SHIPPERS_TOKEN"name: Deploy to Shippers
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install the shippers CLI
run: npm install -g @shippers/cli
- name: Authenticate
run: shippers login "$SHIPPERS_TOKEN"
env:
SHIPPERS_TOKEN: ${{ secrets.SHIPPERS_TOKEN }}
- name: Deploy
run: shippers deploy
# Works the same on GitLab CI, CircleCI, Buildkite, etc. — the CLI
# only needs Node 20+ and a network path to api.shippers.cloud.
npm install -g @shippers/cli
shippers login "$SHIPPERS_TOKEN"
shippers deploy
shippers env push only take effect on the next deploy, not an in-place restart — push them before the deploy step, not after.shippers login step until the account is Pro again.Upgrade to Pro for CLI access, or start free and deploy from the dashboard first.