# Proper way to create a daemon process

**URL:** <https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824>\
**Category:** Python Help\
**Created:** [February 7, 2025, 2:50pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824 "2025-02-07T14:50:36Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [February 7, 2025, 2:50pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/1 "2025-02-07T14:50:36Z")

</div>

I asked [here](https://github.com/python/cpython/issues/82616#issuecomment-2642033035) but I figured the forum would be a better place.

Using Python 3.12+ I would like to create a daemon process that continues running after the current process exits, does not block from I/O and is immune to signals from the current process group. I have the following:

```python
import subprocess
import sys

command = ["foo", "bar"]
if sys.platform == "win32":
    subprocess.Popen(
        command,
        creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS,
        stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        close_fds=True,
    )
else:
    subprocess.Popen(
        command,
        start_new_session=True,
        stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        close_fds=True,
    )

```

Is that sufficient or does one need to manually do that double fork technique on Linux? Perhaps there are more flags on Windows?

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [February 7, 2025, 3:10pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/2 "2025-02-07T15:10:58Z")

</div>

On linux can you have the daemon be a system or user systemd service? You could then start the service when needed.

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [February 7, 2025, 3:16pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/3 "2025-02-07T15:16:46Z")

</div>

Yes, I’m aware that is an option and is the “standard” way but I have a requirement for the short-lived process to be launched with a Python script frequently so I’m trying to figure out how to do this properly on a technical level.

---

<div class="post-metadata">

**Author:** ![JamesParrott](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamesparrott/32/10534_2.png) [@JamesParrott](https://discuss.python.org/u/JamesParrott)\
**Post date:** [February 7, 2025, 4:12pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/4 "2025-02-07T16:12:42Z")

</div>

It’s not so simple, and I don’t know if it’s the proper way, but [this](https://github.com/python/mypy/blob/dd6df2ecb77a06add6ce95d32a3f91cb751cf71e/mypy/dmypy_server.py#L46) is how dmypy starts mypyd.

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [February 7, 2025, 4:23pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/5 "2025-02-07T16:23:01Z")

</div>

The instructions for creating a classic UNIX-style daemon can be found on almost any system by invoking the magic phrase…

```python
man 7 daemon

```

Basically everything described there is doable directly in Python, I’ve done it myself in the past more than once. It’s maybe a dozen lines of code if you want to stick exclusively to the stdlib.

If you don’t mind reaching past the stdlib shelf, [python-daemon · PyPI](https://pypi.org/project/python-daemon/) is quite popular and handles these tasks for you nicely too.

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [February 7, 2025, 4:27pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/6 "2025-02-07T16:27:40Z")

</div>

That doesn’t appear to support Windows. I suppose I could use that only on non-Windows systems but I would prefer a cross-platform approach using only the standard library (even if verbose).

* * *

> [@JamesParrott](#):
>
> [this](https://github.com/python/mypy/blob/dd6df2ecb77a06add6ce95d32a3f91cb751cf71e/mypy/dmypy_server.py#L46) is how dmypy starts mypyd

Ah interesting, it looks like the `startupinfo` option is required on Windows as well.

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [February 7, 2025, 4:37pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/7 "2025-02-07T16:37:23Z")

</div>

> The instructions for creating a classic UNIX-style daemon can be found on almost any system by invoking the magic phrase…
> 
> ```python
> man 7 daemon
> 
> ```

More direct to the initial question, quoting from the above:

```python
5. Call fork(), to create a background process.

6. In the child, call setsid() to detach from any terminal and create
   an independent session.

7. In the child, call fork() again, to ensure that the daemon can never
   re-acquire a terminal again. (This is relevant if the program — and
   all its dependencies — does not carefully specify `O_NOCTTY` on each
   and every single `open()` call that might potentially open a TTY
   device node.)

8. Call exit() in the first child, so that only the second child (the
   actual daemon process) stays around. This ensures that the daemon
   process is re-parented to init/PID 1, as all daemons should be.

```

That part describes the reasons for the double-fork, and terminating the child in order to be sure the grandchild is reparented.

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [February 7, 2025, 4:39pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/8 "2025-02-07T16:39:14Z")

</div>

Yes that is the technique I mentioned in my opening post. Does `start_new_session` do all that?

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [February 7, 2025, 4:42pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/9 "2025-02-07T16:42:11Z")

</div>

> That doesn’t appear to support Windows. I suppose I could use that only on non-Windows systems but I would prefer a cross-platform approach using only the standard library (even if verbose).

Fair, I have programmed BSD-type daemons all of my adult life, but have never written software intended for use on Windows so don’t know how it might differ. From what I’ve heard over the years, I get the impression the steps for creating a traditional background system service on Windows are quite entirely different so likely require completely separate code paths anyway.

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [February 7, 2025, 4:48pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/10 "2025-02-07T16:48:41Z")

</div>

> [@ofek](#):
>
> > [@JamesParrott](#):
> >
> > [this](https://github.com/python/mypy/blob/dd6df2ecb77a06add6ce95d32a3f91cb751cf71e/mypy/dmypy_server.py#L46) is how dmypy starts mypyd
> 
> Ah interesting, it looks like the `startupinfo` option is required on Windows as well.

Upon further reading it looks like there are two ways to achieve what that code does. Are these equivalent?

```python
subprocess.Popen(
    command,
    creationflags=(
        subprocess.CREATE_NEW_PROCESS_GROUP
        | subprocess.DETACHED_PROCESS
        | subprocess.CREATE_NO_WINDOW
    ),
    ...
)

```

```python
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
subprocess.Popen(
    command,
    creationflags=(
        subprocess.CREATE_NEW_PROCESS_GROUP
        | subprocess.DETACHED_PROCESS
    ),
    startupinfo=startupinfo,
    ...
)

```

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [February 7, 2025, 4:48pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/11 "2025-02-07T16:48:47Z")

</div>

> [@ofek](#):
>
> Yes, I’m aware that is an option and is the “standard” way but I have a requirement for the short-lived process to be launched with a Python script frequently so I’m trying to figure out how to do this properly on a technical level.

You are going to need to explain in more detail what you need to achieve.  
Is this a system process or a user process?  
Do you expect to process to survive the user logging out?  
What OS do you need to this to work on? Windows and what else?

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [February 7, 2025, 4:53pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/12 "2025-02-07T16:53:17Z")

</div>

We want to collect telemetry from developer tooling/CLIs but we want collection to have no impact on responsiveness. So my idea is to launch a daemon process that reads from a temporary directory created in the main process. The main process will write data like which command was executed, the exit code and how long it took to files inside that directory. The daemon process will wait for the required files to be written and then send the HTTP payload with support for retries and such.

The target platforms would be wherever our developers are so macOS, Windows, and a few folks on Linux.

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [February 7, 2025, 5:00pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/13 "2025-02-07T17:00:01Z")

</div>

Then you should be able to launch a process and not wait for it to finish.  
The process will be killed when the user logs out.  
No need for magic fork-dance (that’s legacy for system deamons).

I take it doing the logging in a background thread will not work for you?

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [February 7, 2025, 5:03pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/14 "2025-02-07T17:03:37Z")

</div>

> [@barry-scott](#):
>
> I take it doing the logging in a background thread will not work for you?

Right, that won’t work because we are submitting the data to Datadog (ourselves) over HTTP so we don’t want to add any overhead, not even importing the submission library, and we don’t want an error to impact the main process. The main concern is having to retry the network call, I just don’t want to impact CLI timings at all because Python already isn’t the best there.

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [February 7, 2025, 5:03pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/15 "2025-02-07T17:03:43Z")

</div>

> Yes that is the technique I mentioned in my opening post. Does `start_new_session` do all that?

I don’t know, I’ve only ever trusted `os.fork()` in that capacity, since `subprocess.Popen()` seems designed more for spawning children you intend to interact with rather than orphan.

---

<div class="post-metadata">

**Author:** ![dpdani](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dpdani/32/14107_2.png) [@dpdani](https://discuss.python.org/u/dpdani)\
**Post date:** [February 7, 2025, 5:24pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/16 "2025-02-07T17:24:40Z")

</div>

There’s also:

> **[PEP 3143 – Standard daemon process library | peps.python.org](https://peps.python.org/pep-3143/)**
>
> Writing a program to become a well-behaved Unix daemon is somewhat complex and tricky to get right, yet the steps are largely similar for any daemon regardless of what else the program may need to do.

which never got into the stdlib.

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [February 7, 2025, 6:33pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/17 "2025-02-07T18:33:08Z")

</div>

systemd has made the unix deamonise dance no longer required.

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [February 7, 2025, 6:59pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/18 "2025-02-07T18:59:36Z")

</div>

> systemd has made the unix deamonise dance no longer required.

At least on systemd-based Linux distributions (which, to be fair, is the majority of popular Linux distributions now). Not necessarily the case on BSD derivatives, which probably includes MacOSX/Darwin (I do use BSDs a lot, but have only a passing familiarity with Darwin). I guess it’s a question of how cross-platform the cross-platform need is in this case.

---

<div class="post-metadata">

**Author:** ![barry-scott](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@barry-scott](https://discuss.python.org/u/barry-scott)\
**Post date:** [February 7, 2025, 8:56pm UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/19 "2025-02-07T20:56:33Z")

</div>

On macOS you would use a launchd service that is logically the same as systemd, indeed launchd was one of the inspirations for systemd.

Agreed that if you care about BSD then you need to do the old school daemon dance.

---

<div class="post-metadata">

**Author:** ![ofek](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ofek/32/1033_2.png) [@ofek](https://discuss.python.org/u/ofek)\
**Post date:** [July 10, 2025, 2:33am UTC](https://discuss.python.org/t/proper-way-to-create-a-daemon-process/79824/20 "2025-07-10T02:33:01Z")

</div>

For posterity, I just encountered [this issue](https://github.com/python/cpython/issues/85785). So on Windows it should only be the following two flags: `subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.CREATE_NO_WINDOW`
