# Why `chia node start` 's UX is terrible for server-targeted deployments? To provide an easy way for a simple user to start and manage the node, Chia has chosen to use the background daemon approach built up based on multiple components, combining into what they call **Chia Blockchain (chia-blockchain on GitHub)**. This is a really decent approach for newbies and generally the users who are running the nodes on their desktop machines. On paper, it should be easier to manage for servers with it, as the software itself is handling background runtime. But in fact, it is not. ## How are professionals running the software on servers? In reality, almost every professional and reputable enterprise is using supervisors to handle the runtime of the underlying software, which makes the life of system administrators easy and, what is the most important — consistent, as every program is run the same. The simplest, mostly related to newbies example is `systemd`. This is the standard background process supervisor bundled with almost every Linux distribution. `systemd` is something we say when a newbie asks "How to keep a program open even if SSH session was closed." #### Docker More modern people are using containerization, which directly points us to Docker. Docker is not just a supervisor, but something entirely different. It implies bundling the environment along with the software — while calling all of that together a **container**. Docker's best practice is one container = one application, with the advantage of not being required to bother if you have the right environment on the host machine. Docker allows you to run software in a predictable way. In a perfect world, a Docker image implies running a single process (with optional children processes), and in this case, we will have to use a lot of glue in order to make an illusion of it with Chia :sweat_smile:. In this article, I will compare the steps to get a Chia node working to the steps to get an Ethereum's Geth node working under Systemd and Docker. Geth uses the most idiomatic approach ever existed in our world — a single runnable binary with CLI arguments. Bitcoin uses the same if that matters, along with pretty much all coins from the top 10. **Let's get to the actual examples compared to Geth — the Ethereum's node.** # systemd ## Geth (Ethereum Node) Since Geth is a single binary runnable application, there is no easier way to run the node with Systemd. Just copy-paste the simple service template from Google, fill the path to your Geth binary, and you're done. ```conf [Unit] Description=Geth Ethereum Node [Service] ExecStart=/path/to/geth [Install] WantedBy=multi-user.target ``` And that's it. After that, just do `systemctl enable --now geth`, and you already can use graceful stops via `systemctl stop` along with `systemctl status` and another bunch of features a software supervisor provides. The most important thing with supervisors is keeping consistency, which is easily achieved with the Geth node. If you use, say `nginx`, you can interact with it in exactly the same way — `systemctl <action> nginx`. ## Chia node Things are about to get (not very) funny. We will have to use `Type=forking` as the Chia process creates other processes to run the full node. Selecting `Type=forking` means removing all supervisor features and trying to implement them on our own, which, unfortunately, we have to do. So now we have to specify `ExecStart` and `ExecStop` variables in order to guide `systemd` on how to deal with the Chia node. `ExecStart` is what systemd will call when it has received a signal to start a node, and `ExecStop` is what `systemd` will call when it has received a single to shut the node down. So let's build these commands! The command itself will be a script, so yes, we are already diving into what people call "gluing things together" — in this case, poor `systemd` and a Chia node. First, we should activate the Chia virtual environment to access the `chia` CLI tool. This is what we will use to interact with the Chia daemon. The startup script will look like this: ``` source /path/to/chia/venv/bin/activate && chia start node ``` And as to shut the node down: ``` source /path/to/chia/venv/bin/activate && chia stop -d node ``` `-d` there means that we would also want to shut the Chia daemon down to avoid orphan processes and further conflicts when trying to start the node again. So let's glue our ExecStart/Stop configs together along with the bash shell. ```conf ExecStart=/usr/bin/bash -c "source /path/to/chia/venv/bin/activate && chia start node" ExecStop=/usr/bin/bash -c "source /path/to/chia/venv/bin/activate && chia stop -d node" ``` Excellent, now let's bundle things together into a .service file: ```conf [Unit] Description=Chia Node [Service] Type=forking ExecStart=/usr/bin/bash -c "source /path/to/chia/venv/bin/activate && chia start node" ExecStop=/usr/bin/bash -c "source /path/to/chia/venv/bin/activate && chia stop -d node" [Install] WantedBy=multi-user.target ``` And that's pretty much it — `systemctl start` and `systemctl stop` work, but... Unfortunately, this is everything we can do with a bare .service file. We will not be able to track logs, access `systemctl status`, which are one of the most important things when it comes to running a supervised process. But don't get me wrong. Technically it is still possible while using systemd + the scripts above (some glue) + spawning virtual screen using GNU screen (even more glue) + custom software/scripts that will listen for Ctrl+C (and thus `sysctemctl stop`) and handle shutdowns (glue everywhere). Does this sounds too much to you too? Technically we can even write a dedicated Chia node supervisor that will take care the maintenance of virtual screen session, logs, interrupts, initialization, and other stuff which will effectively make an illusion of the Chia node being a single executable program, just as Geth is... But to be honest, I think it would be far easier to remove the part responsible for out-of-the-box background runtime handling of the Chia node, or at least to add an option to disable it and run the Chia node natively just as Geth is run. # Docker ## Geth (Ethereum Node) As above — starting with Geth first. **WARNING:** Dockerfiles here will assume that we already have working software built, be it a single binary or a huge `venv` folder with all `.py` files. This is not something we do in production, and this is done only for the simplicity of these examples. You'll have to download the `geth` binary and place it in the same directory where Dockerfile is located. Dockerfile will like this: ```Dockerfile ADD ./geth /geth ENTRYPOINT [ "/geth" ] ``` And that's it! All things will work out of the box. ## Chia Node First, we will need a supervisor program that will shut the node down when the Ctrl+C signal was sent by the docker daemon, and shut itself down when the node exits because of something else. You can use any programming language to write that program. I will be using Go. Supervisor program source code: ```go // supervisor.go package main import ( "fmt" "os" "os/exec" "os/signal" "time" ) func main() { for { time.Sleep(time.Second) err := exec.Command("chia", "show", "-s").Run() if err == nil { // If there is no error calling chia show -s, then the node is started up. Break this loop break } } // Start a thread monitoring for interrupts go func() { interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) <-interrupt err := exec.Command("chia", "stop", "-d", "node").Run() if err != nil { fmt.Println("chia shutdown error:", err) } else { fmt.Println("chia process shut down successfully") } }() // Periodically check if Chia node is stopped, and if so, shut the supervisor down for { time.Sleep(time.Second) err := exec.Command("chia", "show", "-s").Run() if err != nil { // If there is error calling chia show -s, then the node is down. Break this loop break } } } ``` To build it, ensure that you have `go` installed, and after that, you can do `go build -o chia-node-supervisor supervisor.go`. This will generate a `chia-node-supervisor` program that you will have to place into the directory where Dockerfile will be located. Then, download `chia-blockchain`, build the virtual environment and install dependencies by running `sh install.sh`. Place the `chia-blockchain` folder in the same directory where the Dockerfile is located. And we are not done yet. We will also need to have a custom entrypoint.sh file to pack the mess we have brought here, and imitate a running process. The entrypoint file will look like this: ```bash # entrypoint.sh # Activate the Chia virtual environment source /chia-blockchain/venv/bin/activate # Set the CHIA_ROOT to be used afterwards if [[ -z "${CHIA_ROOT}" ]]; then CHIA_ROOT="$HOME/.chia/mainnet" fi # If chia root dir is not initialized, initialize it. if [[ ! -f $CHIA_ROOT/wallet/db/wallet_peers.sqlite ]]; then chia init fi # Start the actual node chia start node # Run the Chia node supervisor we've made ./chia-node-supervisor ``` Place that entrypoint.sh along with the Dockerfile too. And finally, the Dockerfile: ```Dockerfile ADD ./chia-blockchain/ /chia-blockchain/ ADD ./chia-node-supervisor /chia-node-supervisor # The program to supervise the Chia node process we've built before ADD ./entrypoint.sh /entrypoint.sh ENTRYPOINT [ "/usr/bin/bash", "/entrypoint.sh" ] ``` And we are done for the Dockerfile — but not yet for the container. Once we start it up and `chia init` is called, shut the node container down and customize the following options in the config file: ```diff # Controls logging of all servers (harvester, farmer, etc..). Each one can be overriden. logging: &logging - log_stdout: False # If True, outputs to stdout instead of a file + log_stdout: True # If True, outputs to stdout instead of a file log_filename: "log/debug.log" - log_level: "WARNING" # Can be CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET + log_level: "INFO" # Can be CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET log_maxfilesrotation: 7 # Max files in rotation. Default value 7 if the key is not set log_syslog: False # If True, outputs to SysLog host and port specified log_syslog_host: "127.0.0.1" # Send logging messages to a remote or local Unix syslog log_syslog_port: 514 # UDP port of the remote or local Unix syslog ``` After that, start the node, and the logging will work. "What a journey to get it working!" I would say. Now, we have start/stop, status functionality, and logging available. And all of that was done to compensate for the Chia daemon — a "feature" they have called it... # Summary Chia Node needs to eliminate the daemonized approach as soon as possible. Just look at the pains of running the software provided by me above. This has definitely contributed into the XCH price drop a lot. And to everyone reading this, do not consider this a guide to get a Chia node working under systemd/Docker. I have tested none of the snippets above.