Angular FTP Deployment Setup

How to add the node deploy.js FTP deployment script to another Angular project.


1. Install dependencies

npm install --save-dev ftp-deploy dotenv

2. Add deploy.js to the project root

Create deploy.js next to package.json:

const FtpDeploy = require("ftp-deploy");
const ftpDeploy = new FtpDeploy();
require('dotenv').config();

const config = {
    user: process.env.FTP_USER,
    password: process.env.FTP_PASSWORD,
    host: process.env.FTP_HOST,
    port: process.env.FTP_PORT || 21,
    localRoot: __dirname + "/dist/<your-app-name>",   // <-- update this
    remoteRoot: process.env.FTP_REMOTE_ROOT || "/",
    include: ["*", "**/*"],
    exclude: [],
    deleteRemote: false,  // set true to wipe remoteRoot before uploading
    forcePasv: true,
    sftp: false           // set true for SFTP instead of FTP
};

console.log("Starting deployment to " + config.host + "...");

ftpDeploy
    .deploy(config)
    .then(() => console.log("Deployment finished successfully!"))
    .catch((err) => {
        console.error("Deployment error:", err);
        process.exit(1);
    });

localRoot must match the Angular build output folder. Check angular.json under projects.<name>.architect.build.options.outputPath for the correct path.

How files are placed on the server

ftp-deploy uploads the contents of localRoot, not the folder itself. The folder name is stripped and files land directly in remoteRoot:

localRoot:  dist/my-app/index.html
remoteRoot: /public_html
→ result:   /public_html/index.html   (NOT /public_html/my-app/index.html)

Warning: deleteRemote: true

Setting deleteRemote: true deletes every file and subfolder inside remoteRoot before uploading — including files that have nothing to do with this project (e.g. .htaccess, subfolders from other apps, etc.). The remoteRoot folder itself is kept, but completely emptied.

There is no selective deletion. If you share remoteRoot with other content, leave deleteRemote: false and let new uploads overwrite existing files instead.


3. Create .env in the project root

FTP_USER=your_ftp_username
FTP_PASSWORD=your_ftp_password
FTP_HOST=ftp.yourdomain.com
FTP_PORT=21
FTP_REMOTE_ROOT=/public_html

4. Add .env to .gitignore

.env

Never commit .env — it contains plaintext credentials.


5. Add an npm script (optional)

In package.json:

"scripts": {
  "deploy": "ng build --configuration production && node deploy.js"
}

Then run:

npm run deploy

Or run the steps separately:

npm run build      # produces dist/<your-app-name>
node deploy.js     # uploads dist output via FTP

Configuration reference

Key Default Notes
FTP_USER FTP account username
FTP_PASSWORD FTP account password
FTP_HOST FTP server hostname or IP
FTP_PORT 21 Use 22 for SFTP
FTP_REMOTE_ROOT / Remote path to upload into
localRoot (hardcoded) Must match Angular's outputPath
deleteRemote false Set true to delete all contents of remoteRoot before upload — including files unrelated to this project. The folder itself is preserved but fully emptied. Boolean only; no selective deletion available.
sftp false Set true and FTP_PORT=22 to use SFTP

Troubleshooting

  • Blank remote folder after deploy — check that localRoot matches the actual build output directory. Run npm run build and confirm the folder exists under dist/.
  • Connection refused — verify FTP_HOST and FTP_PORT. Some hosts require passive mode (forcePasv: true) which is already on by default.
  • Permission denied — confirm the FTP user has write access to FTP_REMOTE_ROOT.
  • .env not loaded — make sure deploy.js is run from the project root (node deploy.js, not node path/to/deploy.js from elsewhere).