If I ran the nohup node /tmp/test-app/index.js > /tmp/test-app/nohup.out 2>&1 & command in my Linux console
First of all - your playbook targets all hosts. Since you're saying that the playbook executes and marks the task as changed, you also have an inventory but you did not post it. So, if your controller is not included in that inventory (and by default it is not), the result is expected since Ansible actually does not perform this task on it.
I have checked /var/log/messages and have verbose logged the Playbook execution with -vvv.
Regardless of the first point, it's worth to check the file where the stdout is redirected, especially when you're redirecting the stderr to the same location - this approach masks the actual result of your task and should be used very carefully or avoided at all.
For example, if we take the current LTS version of NodeJS (20.12.0), the sample NodeJS server code from https://nodejs.org/en, and run it with your command "as is", we'll see the following in /tmp/test-app/nohup.out:
(node:7165) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use `node --trace-warnings ...` to show where the warning was created) /private/tmp/test-app/index.js:2 import { createServer } from 'node:http'; ^^^^^^ SyntaxError: Cannot use import statement outside a module at internalCompileFunction (node:internal/vm:128:18) at wrapSafe (node:internal/modules/cjs/loader:1280:20) at Module._compile (node:internal/modules/cjs/loader:1332:27) at Module._extensions..js (node:internal/modules/cjs/loader:1427:10) at Module.load (node:internal/modules/cjs/loader:1206:32) at Module._load (node:internal/modules/cjs/loader:1022:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12) at node:internal/main/run_main_module:28:49 Node.js v20.12.0
If we change the script name to server.mjs, the server will be started with no issues:
TASK [Start the demo app in the background] ****************************************************************************************************** changed: [localhost] TASK [Display the result] ****************************************************************************************************** ok: [localhost] => shell_nohup_result: changed: true cmd: nohup node /tmp/test-app/server.mjs > /tmp/test-app/nohup.out 2>&1 & delta: '0:00:00.007461' end: '2024-04-01 20:37:41.282527' failed: false msg: '' rc: 0 start: '2024-04-01 20:37:41.275066' stderr: '' stderr_lines: [] stdout: '' stdout_lines: [] TASK [Check the process] ****************************************************************************************************** changed: [localhost] TASK [Display the processes] ****************************************************************************************************** ok: [localhost] => msg: - alexander 7407 8.8 0.3 34836824 24976 s002 S+ 8:37PM 0:00.08 node /tmp/test-app/server.mjs TASK [Check the logs] ****************************************************************************************************** ok: [localhost] => msg: Listening on 127.0.0.1:3000
... What's wrong with the nohup method I am doing?
Apart from what's described above, once you try to run the same playbook again, you'll be surprised again:
TASK [Check the logs] ****************************************************************************************************** ok: [localhost] => msg: |- node:events:496 throw er; // Unhandled 'error' event ^ Error: listen EADDRINUSE: address already in use 127.0.0.1:3000 at Server.setupListenHandle [as _listen2] (node:net:1897:16) at listenInCluster (node:net:1945:12) at doListen (node:net:2109:7) at process.processTicksAndRejections (node:internal/process/task_queues:83:21) Emitted 'error' event on Server instance at: at emitErrorNT (node:net:1924:8) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) { code: 'EADDRINUSE', errno: -48, syscall: 'listen', address: '127.0.0.1', port: 3000 } Node.js v20.12.0
Obviously, it's not an idempotent result. Since idempotency is one of the main things that Ansible is intended to promote, you might want to somehow fix this issue. For example, just stop the process before attempting to start it again.
Please provide a tested, working solution.
It is a bit more complex that one might think. You would need to track the PID, the port number it's using, choose what command to use depending on the environment, handle its return codes because they might vary depending on the application state, decide whether you want to apply the rolling update (I doubt it's the case right now but still), and so on.
Consider the following playbook as the simplest working example (if pkill is available on your system):
--- - name: Test app hosts: localhost gather_facts: false connection: local vars: tmp_dir: '/tmp/test-app' tasks: - name: Create the test directory file: path: '{{ tmp_dir }}' state: '{{ item }}' loop: - absent - directory - name: Create an HTTP server using NodeJS (see https://nodejs.org/en for details) copy: dest: '{{ tmp_dir }}/server.mjs' content: | // server.mjs import { createServer } from 'node:http'; const server = createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello World!\n'); }); // starts a simple http server locally on port 3000 server.listen(3000, '127.0.0.1', () => { console.log('Listening on 127.0.0.1:3000'); }); // run with `node server.mjs` - name: Stop the NodeJS processes command: 'pkill node' register: command_pkill_result failed_when: command_pkill_result.rc not in [0, 1] - name: Start the demo app in the background shell: 'nohup node /tmp/test-app/server.mjs > /tmp/test-app/nohup.out 2>&1 &' args: chdir: '{{ tmp_dir }}' register: shell_nohup_result - name: Display the result debug: var: shell_nohup_result - name: Check the processes command: 'ps aux' register: command_ps_result - name: Display the NodeJS processes debug: msg: '{{ command_ps_result.stdout_lines | map("regex_search", ".*node.*") | select }}' - name: Check the logs debug: msg: '{{ lookup("file", tmp_dir + "/nohup.out") }}' - name: Check the actual server uri: url: http://127.0.0.1:3000 return_content: true register: uri_result - name: Show what the app is trying to say debug: var: uri_result.content
That answer is from 2014 (10 years ago). Are you sure that's still the right way?
Let's put it this way:
- daemonizing a service is a pretty viable solution in general
- you did not introduce any other details on your specific situation and limitations.
To sum these statements up - there is no "right" way in the currently described environment.
It we're talking about just running a local instance of a simple application for educational purposes - your solution works fine once the obvious issues get fixed. You might also want to look into the differences between shell and command modules:
If you want to build a production-grade application this way, of course you'll have to consider much more details, and the description of those would result in a course on the SDLC. Regarding the tools - you just need to limit the choices to several generic ones which have been around for years and tested thousands times, and select the one most suitable for your environment. For example, you might choose to run your server in a pre-built NodeJS container instead. Ansible is capable of controlling the containers, too, as well as to control Docker Compose, Helm, Kubernetes, and other tooling around the containers. But that sounds already a bit off-topic for the initial question.
servicemodule.nohupmethod I am doing?node.jsApp Withsystemd.