Taking an Electron.js application to production is not just about running a single command. If you have already worked with Electron, your app probably works perfectly in development… until you try to generate an executable and the problems begin.
In this guide, I am going to explain how to handle deployment, packaging, and distribution of an Electron app in a realistic way, relying on both best practices and mistakes I made myself while testing different tools. The goal is not just to make it work, but for you to understand what you are doing and why.
We are going to learn not how to update our Electron project, but how to generate a production-ready application through an executable; for this, we will use the following package:
What it really means to take an Electron app to production
Why there is no "magic command" for Electron deployment
One of the first reality checks I had was understanding that Electron does not have a single deployment flow. There is no such thing as npm run deploy that magically generates an .exe, a .dmg, and a .deb ready for distribution.
Deployment in Electron is a process, not a command:
- Compile your code (renderer + main)
- Package the app
- Create installers
- Sign (if applicable)
- Distribute and update
Once I accepted this, I stopped fighting with the tools and started using them better.
Key differences between development and production environments in Electron
In development:
- Relative paths "work"
- Assets are loaded from the file system
- Node and Electron are always available
In production:
- Code is packaged
- Paths change
- Some files stop existing where you thought they were
- Windows is much less permissive
Several bugs had nothing to do with Electron itself, but with assuming the environment was the same as development. And it is not.
Tools for packaging and distributing Electron
Electron Forge: what it promises and when it makes sense to use it
Electron Forge is the "official" tool. It promises:
- Quick scaffolding
- Complete build pipeline
- Integrated publishing
And all of that is true… in the ideal case. When I tried it, it worked well to start, but as soon as I wanted to control the output (especially on Windows), the experience became frustrating: very slow builds, processes that seemed hung, and documentation that omitted key details.
Forge makes sense if:
You are just starting
You don't need much control
You accept the abstraction
Electron Builder: why it is usually the most predictable option
After struggling with Forge, I ended up using electron-builder, and the difference was clear: less magic, more control.
Electron-builder:
- Is explicit
- Lives in package.json
- Generates real installers
- Integrates well with CI/CD
It's not perfect, but it is predictable, and in production, that is worth its weight in gold.
Electron Forge vs Electron Builder: comparison
| Aspect | Forge | electron-builder |
|---|---|---|
| Initial ease | High | Medium |
| Build control | Low | High |
| Windows (NSIS) | Opaque | Configurable |
| Documentation | Idealistic | Technical |
| Real production | Limited | Solid |
If I had to start a serious Electron project today, I would go straight with electron-builder.
Preparing the Electron project for packaging
Typical structure of a production-ready project
A project prepared for deployment usually clearly separates:
- main process
- renderer
- assets
- build output
This isn't just a whim. Packaging works better when each part has its responsibility clear.
Main process, renderer, and preload: what affects deployment
The preload script is especially important. When packaging:
- API access is restricted
- Execution context changes
- Silent errors appear
In one of my builds, the app would start… but it did nothing. The problem was in the preload, not the renderer. In production, these errors are harder to detect if you don't anticipate them.
Common problems with paths and assets in production
This is one of the most common mistakes: using paths that only exist in dev.
When you package:
- __dirname no longer points where you think
- Assets can go inside the asar
- Absolute paths stop working
Here I learned to always test intermediate builds, not just the final installer.
Packaging an Electron application with electron-builder
Correct installation and dependencies (common errors included)
Electron-builder must be in devDependencies. If you place it in dependencies, the build fails. It happened to me, and the error is not particularly friendly.
Correct installation:
$ npm install electron-builder -DThis detail, although small, completely blocks deployment if you don't know it.
It is important to note that you must install this package in every Electron.js project when you want to generate application executables, as it is not a dependency installed globally but locally.
Once installed, we configure the following commands for MacOS:
package.json
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"start": "DEBUG=true electron .",
"dev:macos": "electron-builder --macos --dir",
"pro:macos": "electron-builder --macos"
},For Linux:
package.json
"scripts": {
***
"dev:linux": "electron-builder --linux --dir",
"pro:linux": "electron-builder --linux"
},And for Windows:
package.json
"scripts": {
***
"dev:windows": "electron-builder --win --dir",
"pro:windows": "electron-builder --win"
},Scripts by operating system (Windows, macOS, and Linux)
A strategy that worked very well for me was separating scripts:
- --dir for testing
- full build for production
This allows validating the package before generating the installer, saving a lot of time.
Generating the build
Then it is possible to run any of the commands according to your operating system; for example:
$ npm run dev:macosTo only generate the package folder without actually packaging it; this is useful for testing purposes
Or
$ npm run pro:macosTo package into a distributable format (e.g., dmg, windows installer, deb package).
By running the previous command, we will have generated the project as an executable:
<proyect>/distWhen running the executable, which in the case of MacOS would be the DMG, in the case of Windows, it would be the EXE, and in the case of Linux, it depends on the flavor you are using, we will see the application.
Another very important detail is that, depending on the project you are carrying out, you might have to make additional modifications since the workspace is different; especially with the integration of external files whose directory specified at the time of developing the application does not correspond with the one used in production. Throughout the book, we will see some examples.
Generate executables and installers: exe, dmg, deb, and rpm
With electron-builder you can generate:
- .exe (NSIS)
- .dmg
- .deb
- .rpm
Everything from the same project, as long as you have the right environment. This is where electron-builder really shines compared to Forge.
Frequent errors when packaging Electron applications
electron-builder in dependencies vs devDependencies
This error is so common that it deserves to be mentioned twice. If you see a message like:
Package "electron-builder" is only allowed in devDependencies
Now you know why it happens.
Problems with NSIS, permissions, and Windows builds
Windows is, by far, the most problematic system:
- Permissions
- Antivirus
- Blocked installers
- Slow builds
Simplifying the NSIS configuration at the start saves many headaches.
Apps that work in dev but fail in production
This is a classic. And almost always the cause is one of these:
- Paths
- Environment variables
- Dependencies not included
- APIs not available
When something fails in production, assume first that the environment changed, not that Electron "is broken."
Distribution of the Electron application
Options for distributing your app (web, GitHub, own servers)
You can distribute your app:
- From your website
- Using GitHub Releases
- From your own server
You don't need a store to start. In fact, many Electron apps are distributed directly as downloadable installers.
Automatic publishing and CI/CD for Electron
Automating the build changes everything. With CI:
- You avoid inconsistent local builds
- You generate reproducible releases
- You centralize errors
Here electron-builder integrates very well with GitHub Actions and other pipelines.
Automatic updates in Electron applications
Electron supports auto-updates, but:
- It's not magic
- It requires infrastructure
- It must be planned from the start
My recommendation: don't start here. First, get a stable build.
Final tips before generating your executable
Quick checklist before doing the production build
- ✔️ Paths reviewed
- ✔️ Assets accessible
- ✔️ electron-builder in devDependencies
- ✔️ Build with --dir tested
- ✔️ Logs enabled
What I would do differently if I started an Electron project today
If I started today:
- I would use electron-builder from day one
- I would test intermediate builds before the installer
- I would assume Windows is going to give problems
- I would design thinking about production, not just dev
Conclusion
Deployment, packaging, and distributing an app in Electron.js is not trivial, but it is not unapproachable either. The key is to understand the process, choose the tools well, and assume that making mistakes is part of the journey.
If I have learned anything, it's that Electron works well in production, as long as you also do your part.