r/learnprogramming • • 12h ago

How do I properly deploy this web server in production mode?

I set up a web server using https://github.com/seanpmaxwell/express-generator-typescript

I now need to deploy the production build to a remote server. I can generate the prod build with `npm run build` and then run it with `npm start` and it runs on my local machine. Is it possible to generate something that can be compiled before being sent over to the remote server? I'd like to run the web server in prod mode, but without putting the decompiled source code on the remote server. I don't want the source code over on that remote server because if the server gets compromised, I don't want anyone to be able to read the source code. New to web dev, so thanks for being patient.

5 Upvotes

11 comments sorted by

2

u/Dameon_ 12h ago

First off, what do you think Typescript gets compiled to? Hint: It's a very readable language.

Second off, if there's anything in the source code that would compromise security if a hacker could read the source code, you're already in trouble. That's called security through obscurity, and it's not going to protect your server. If the server is compromised to the point where somebody's browsing through its local files, you're also already in trouble to an extent that hiding your source is not going to protect you.

1

u/Reasonable-Job4205 11h ago edited 11h ago

Javascript?

The source code has hardcoded credentials for a service account that is used to run transactions against the database. This service account is locked down pretty heavily on the SQL side: it's set to public role and only allowed to run a fixed list of stored procedures. I wasn't sure how to set this up to use Windows auth for its transactions against the DB. If you have any ideas there, I'm all ears.

Would it be more secure to store the credentials in the remote server's environment variables? Because looking at the docs for node and the mssql package, it looks like I could read the variables with `process.env.VARIABLENAME`.

2

u/Dameon_ 10h ago

Correct, Javascript, and so perfectly readable source code is still stored on the server. It may be a little harder to read, but a halfway competent reader will still be able to find and extract anything sensitive.

Yes, your environment variables are more secure. Never, ever, EVER store credentials in source code. That means you're storing sensitive credentials everywhere that source code is sent. At least with environment variables they're stored only on the server, and depending on how the server is set up they're only available to the account the server is running on. There's better, more secure ways, but that depends on exactly how your project is set up.

1

u/Reasonable-Job4205 10h ago

I'll set it up with environment variables, but what are the more secure ways?

1

u/Dameon_ 10h ago

It depends a lot on server setup. For example, if you were on Azure you could set up a keystore that could be accessed using managed identity. For your case, environment variables are probably good enough.

1

u/mandzeete 9h ago

Use Vault: https://developer.hashicorp.com/vault/tutorials/auth-methods/approle?variants=vault-deploy%3Aselfhosted Credentials are inside a Vault container that your web application communicates with. Even better when you are using dynamic secrets as Vault will rotate these over time.

1

u/Asly97 10h ago

On the Windows auth part, since that gets rid of the stored password completely:

The mssql package uses the tedious driver by default, which can't do integrated auth on its own. Swap to the msnodesqlv8 driver (npm i msnodesqlv8, then require('mssql/msnodesqlv8') instead of require('mssql')) with a config roughly like:

const config = {
  server: 'yourSqlServer',
  database: 'YourDb',
  driver: 'msnodesqlv8',
  options: { trustedConnection: true }
}

That connects as whatever Windows account the node process is running under. So the other half is running the app as a Windows service under that service account (NSSM or node-windows both do this), ideally a gMSA so nobody ever has to know or rotate its password. Then create a SQL login for that Windows account and give it the same execute-only permissions you already have.

Catches: msnodesqlv8 needs the Microsoft ODBC Driver for SQL Server installed on the box, and this only works if the app server is a Windows machine joined to the same domain. If it's Linux, env vars (or a secrets store) is the way.

For the deploy itself you only need to copy the compiled build output plus package.json and package-lock.json, then run npm ci --omit=dev on the server. The .ts files never have to go over.

2

u/BuildingShoddy8672 9h ago

Mechanical part first, because it changes the plan.

npm run build already gives you the program: TypeScript's types are erased, so the output is plain JavaScript and no build step makes it unreadable. Beyond that you have obfuscation with rising cost. Bundle and minify with esbuild or terser (a few minutes to undo for anyone who cares), compile to V8 bytecode with bytenode, or ship a single binary via pkg, nexe, or Node's own SEA support. Those stop casual reading, but the code can still be recovered off a machine you own, so treat them as speed bumps rather than protection.

What genuinely works is not shipping the logic. If a piece of code must stay secret, keep it on a service you control and have the remote server call it over HTTP.

The deployment pieces you are actually missing:

  • Build locally or in CI and ship the built directory. Never run the build on the remote, that is also where dev dependencies would end up.
  • Run it under a process manager instead of npm start in an open shell: a systemd unit or pm2, restart on crash, start on boot.
  • NODE_ENV=production, listen on 127.0.0.1, and put nginx or Caddy in front for TLS so 80 and 443 are the only doors open.
  • Secrets as environment variables or a .env file outside the repo (chmod 600), never baked into the artifact you ship.
  • Run as a non-root user and firewall everything except 22, 80, 443.

Ranked by what will actually hurt you: leaked credentials and an unpatched OS come first, readable source a distant last.

2

u/Different_Pain5781 1h ago

TypeScript gets compiled into plain JavaScript before it runs on the remote machine. Just build it locally and upload the compiled files from your dist folder instead of shipping the raw source. Boot dev covers this kind of backend deployment stuff too.