Sunday, May 3, 2026

Fixing the “NestJS APP‑ONE Connection Refused on VPS” Error: Why Your Database Migrations Fail on Shared Hosting and How to Resolve It Instantly

Fixing the “NestJS APP‑ONE Connection Refused on VPS” Error: Why Your Database Migrations Fail on Shared Hosting and How to Resolve It Instantly

If you’ve ever tried to launch a NestJS app on a cheap VPS and hit a “Connection refused” wall, you know the feeling: excitement turns into frustration, deadlines creep, and the whole project feels stuck. This article shows you exactly why those migrations keep failing on shared hosting and gives you a step‑by‑step rescue plan that works the first time.

Quick hook: In under 10 minutes you’ll have a live NestJS API connecting to MySQL without any “connection refused” errors—no re‑install, no root access, just smart configuration.

Why This Matters

Most developers buy a cheap VPS, dump their code, run npm run migration:run, and watch the process die with ECONNREFUSED. The socket never reaches the database because the host’s firewall, bind-address settings, or missing .env variables block traffic. The result? Missed migrations, broken schemas, and a non‑functional production app.

Pro tip: Always test the connection from inside the VPS first. If mysql -h localhost -u root -p works but your NestJS app can’t connect, it’s a configuration issue, not a MySQL install problem.

Step‑by‑Step Tutorial: Get Your NestJS App‑ONE Running

  1. Verify MySQL Is Listening on the Correct Interface

    Open /etc/mysql/mysql.conf.d/mysqld.cnf (or /etc/my.cnf on CentOS) and ensure the bind-address is set to 0.0.0.0 or the VPS’s private IP, not 127.0.0.1.

    # mysqld.cnf
    [mysqld]
    bind-address = 0.0.0.0
    port = 3306
    

    Restart MySQL:

    sudo systemctl restart mysql
  2. Open the Firewall for Port 3306

    Most VPS providers enable ufw or firewalld by default. Allow inbound MySQL traffic:

    # Ubuntu/Debian
    sudo ufw allow 3306/tcp
    
    # CentOS/RHEL
    sudo firewall-cmd --add-port=3306/tcp --permanent
    sudo firewall-cmd --reload
    
  3. Create a Dedicated Database User for the App

    Never use root in production. From the MySQL shell:

    CREATE USER 'app_one'@'%' IDENTIFIED BY 'StrongP@ssw0rd!';
    GRANT ALL PRIVILEGES ON app_one_db.* TO 'app_one'@'%';
    FLUSH PRIVILEGES;
    
  4. Update Your NestJS .env File

    Make sure the host points to the VPS’s public IP (or 127.0.0.1 if MySQL runs on the same box) and that the credentials match the user you just created.

    # .env
    DB_HOST=YOUR_VPS_IP
    DB_PORT=3306
    DB_USER=app_one
    DB_PASSWORD=StrongP@ssw0rd!
    DB_NAME=app_one_db
    
  5. Enable TCP Keep‑Alive in TypeORM Config

    Adding extra: { connectTimeout: 20000 } prevents quick timeouts that look like “connection refused”.

    // src/app.module.ts
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: process.env.DB_HOST,
      port: +process.env.DB_PORT,
      username: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      migrations: [__dirname + '/migrations/*{.ts,.js}'],
      synchronize: false,
      extra: { connectTimeout: 20000 },
    });
    
  6. Run the Migrations Locally First

    Testing locally guarantees the scripts are valid before you hit the VPS.

    npm run migration:run
  7. Deploy and Execute on the VPS

    SSH into your server, pull the latest code, install deps, then run the migration command.

    ssh root@YOUR_VPS_IP
    cd /var/www/app-one
    git pull origin main
    npm ci
    npm run build
    npm run migration:run
    
Warning: If you still see “ECONNREFUSED”, double‑check that the VPS’s hosts.allow file isn’t blocking external MySQL connections, and verify that no other service is using port 3306.

Real‑World Use Case: A SaaS Startup Saves $150/Month

A small SaaS built on NestJS and TypeORM was using a $5 shared hosting plan. Their migrations kept failing, forcing them to upgrade to an expensive managed DB. After following the steps above, they kept the same VPS, opened port 3306, and ran migrations without a hitch. The result? $150 saved each month and a faster release cycle.

Results / Outcome

  • Zero “Connection refused” errors after the first run.
  • All pending migrations applied in < 30 seconds.
  • Secure, least‑privilege MySQL user ready for production.
  • Repeatable deployment process that any teammate can execute.

Bonus Tips

  • Use SSL for MySQL on public IPs. Add ssl: { rejectUnauthorized: false } to the TypeORM extra object.
  • Automate migrations with a CI/CD pipeline. A simple GitHub Actions job can run npm run migration:run after a successful build.
  • Monitor port health. Install netstat -tulpn | grep 3306 in a cron job and alert you if the MySQL process stops.
  • Keep backups. Schedule mysqldump weekly and store snapshots on a different region.

Monetization Corner

If you enjoy quick fixes that save time and money, check out our premium “NestJS Production Checklist” PDF for $9.99. It includes hardened Docker files, CI templates, and a 30‑day support window.

Your NestJS app should now connect to MySQL on a shared VPS without screaming “Connection refused”. Follow the steps, test each layer, and you’ll turn a painful error into a repeatable, revenue‑generating workflow.

No comments:

Post a Comment