Quick Start

Install the CLI, register an app, wire up the client, and deliver your first over-the-air update.


The fastest path from nothing to a bundle running on a device. Pick the Capacitor path or the Electron path — the CLI steps are the same, only the client integration differs.

Prerequisites

  • Node.js 20 or newer
  • An account on the dashboard at https://app.deployyour.app
  • A Capacitor 6+ project, or an Electron 28+ project

Install the CLI

npm install -g @deploy-your-app/cli
dya --version

Capacitor

1. Run the guided setup

From your project root:

dya setup

dya setup is interactive and needs a TTY. It walks through logging in, picking an organization, creating or selecting the app, writing the .deployyourapp project config, generating RSA key pairs, installing @deploy-your-app/capacitor-update-manager, writing the plugin configuration, and running npx cap sync.

If you prefer to run the steps yourself, or you are scripting this in CI, see the manual setup section below.

2. Confirm each successful launch

Add one call to your app's startup path, after your UI has rendered:

import { DeployYourApp } from '@deploy-your-app/capacitor-update-manager';

await DeployYourApp.notifyAppReady();

If this is not called within appReadyTimeout (10000 ms by default) after an update is activated, the plugin rolls back to the previous bundle. That is the safety net for a bundle that fails to boot — leave it in.

That is the whole integration. With the default autoUpdate: true, the plugin checks on launch and every checkInterval seconds (600 by default), downloads new bundles, verifies them, and activates them according to applyMode (whenIdle by default, which loads the bundle on the next launch).

3. Build and deploy

npm run build
dya deploy --channel production

Realistic output:

ℹ Assets: /Users/you/my-app/dist/spa
✔ App: My App (com.example.app)
ℹ Version: 2026.0731.142530
ℹ Channel: production
ℹ Platform: capacitor
✔ Manifest: 41 files
✔ Archive: 1.4 MB
✔ Signed with RSA-4096
✔ Uploaded: 2026.0731.142530 (1.4 MB)
✔ Deployed to production at 100% rollout

✓ Deploy complete! Version 2026.0731.142530 is live.

The version is generated from the current time when you do not pass --version.

Devices on the production channel receive the update on their next check.

Electron

1. Log in and register the app

dya login
dya apps create --name "My App" --app-id com.example.app --platform electron

Creating an app also creates a production channel and makes it the default.

2. Initialize the project and generate keys

From your project root:

dya init --app-id com.example.app
dya keys generate

dya init writes the .deployyourapp config file and auto-detects your web assets directory. dya keys generate writes four PEM files into the current directory and uploads the two public keys to the server:

File Keep it
dya-signing-private.pem On your machine and in CI only. Never commit it.
dya-signing-public.pem Uploaded to the server; also goes in your client config as publicKey.
dya-encryption-public.pem Next to the CLI; used to encrypt bundles at deploy time.
dya-encryption-private.pem Embedded in your app binary; this is the decryption key.

Encryption is optional. If dya-encryption-public.pem is present when you deploy, the bundle is encrypted before upload.

3. Install and wire up the updater

npm install @deploy-your-app/electron-update-manager

In your main process:

import path from 'node:path';
import { app, BrowserWindow } from 'electron';
import { ElectronUpdateManager } from '@deploy-your-app/electron-update-manager/main';

const updater = new ElectronUpdateManager(
  {
    appId: 'com.example.app',
    channel: 'production',
    autoUpdate: true,
  },
  app.getAppPath()
);

app.whenReady().then(async () => {
  await updater.initialize();

  const win = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
    },
  });

  // Required: without a window reference the updater cannot activate a bundle.
  updater.setWindow(win);
  win.loadFile(updater.getBundlePath());

  app.on('before-quit', () => updater.destroy());
});

In your preload script:

import '@deploy-your-app/electron-update-manager/preload';

In your renderer, once the UI has rendered:

import { getUpdaterApi } from '@deploy-your-app/electron-update-manager/renderer';

await getUpdaterApi().notifyAppReady();

4. Build and deploy

npm run build
dya deploy --channel production --target electron

--target electron is required. The server only returns a bundle whose target platform matches the requesting client, so a bundle deployed with the default capacitor target is never served to a desktop app.

Manual setup without dya setup

The Capacitor equivalent of dya setup, one command at a time:

dya login
dya apps create --name "My App" --app-id com.example.app --platform both
dya init --app-id com.example.app
dya keys generate
npm install @deploy-your-app/capacitor-update-manager
npx cap sync

Then add the plugin block to capacitor.config.json:

{
  "plugins": {
    "DeployYourApp": {
      "appId": "com.example.app",
      "channel": "production"
    }
  }
}

Set publicKey to the contents of dya-signing-public.pem to enable signature verification on the device, and encryptionPrivateKey to the contents of dya-encryption-private.pem if you deploy encrypted bundles. Do not commit either value to a public repository.

Verifying it worked

  • The dashboard shows the deployment under the app's Deployments tab.
  • The device appears under Devices after its first update check.
  • dya deploy prints the version it uploaded; the same version appears on the channel.

If a device is not picking up the update, check that its appId matches the registered app, that the channel name matches, and that the bundle's target platform matches the client.

A client whose platform the app does not target gets an HTTP 409 with a message naming the mismatch, rather than a silent "no update". The usual cause is pointing a desktop build at an app registered as ios, android, or both (or the reverse). An app targets either the Capacitor family or Electron — never both — because a channel serves one active bundle and the two bundle types are not interchangeable. Register a second app for the other platform.

Next steps


Previous
Introduction
Next
Core Concepts