Blog

All Blog Posts  |  Next Post  |  Previous Post

Server Hosting in TMS Sparkle: Console, Windows Service or Linux Daemon

Today

Your REST API is finished. The endpoints work, the queries are fast, it runs beautifully when you press F9. Now put it in production.

That's usually where a Delphi server project gets complicated. The application that was so pleasant to debug has to become a Windows service that starts at boot and runs with nobody logged in. So you add a second project with a TService, and now you maintain two versions of the same server - one you can debug and one you can ship. Somewhere in there appears a {$IFDEF DEBUG} that nobody wants to touch. And if the customer wants it on Linux, that's a third project.

None of that is your business logic. It's hosting: plumbing that has nothing to do with what your server actually does, but that stands between you and a deployed application.

The next version of TMS Sparkle removes it. It adds a hosting layer so that a server is one single project, one executable, that runs unchanged as a console application while you develop, as a Windows service in production, and as a Linux daemon. Three new IDE wizards create servers in this shape for Sparkle, XData and RemoteDB, so you start from it instead of building up to it.


A quick refresher

TMS Sparkle is a high-performance HTTP server framework for Delphi. It's the foundation under TMS XData, a framework for building REST APIs, TMS RemoteDB, a framework for remote database access over HTTP, TMS Sphinx, a full OAuth/OpenID Connect implementation to secure your APIs, among other products. Whatever you build with these products is, at the bottom, a Sparkle server, so everything in this article applies to all those.


One project, three run modes

The new unit Sparkle.Host turns a console program into a server host. The same binary runs:

  • as an interactive console application, on Windows and Linux - what you get when you press F9;
  • as a Windows service, registered with the Service Control Manager by the executable itself;
  • as a systemd daemon on Linux, with a unit file the executable prints for you.

The mode is chosen at runtime, by the command line. Not at compile time: no conditional defines, no separate service project, no TService descendant, no VCL form with Start and Stop buttons. One .dproj to build, debug, version and deploy.

What you deploy is exactly what you debugged. The host takes care of everything that differs between the three modes - the Service Control Manager handshake, POSIX signals, graceful shutdown, exit codes - and your server code never learns which mode it is in.


Start with a wizard

Choose File > New > Other in the IDE, and under Delphi Projects open the TMS BIZ category. All TMS BIZ wizards are now grouped there, including three that create a ready-to-run hosted server: TMS Sparkle Server, TMS XData Server and TMS RemoteDB Server.

Pick one and you get a console project targeting Win32, Win64 and Linux64, made of two files. The program file is one line of code:

program XDataConsole;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Sparkle.Host,
  ServerModuleUnit in 'ServerModuleUnit.pas' {ServerModule: TDataModule};

begin
  RunHost(TServerModule);
end.

The second file is a data module with the server components already dropped and wired, ready for you to configure. That data module is your application: RunHost creates it when the host starts and frees it when the host stops, so you write your endpoints and your logic there and ignore everything else.

Press F9. A console window opens:

2026-09-11 10:15:02 [info] Now listening on: http://localhost:2001/tms/xdata

Open the address in a browser, press Ctrl+C to stop it. Requests in progress finish, the server shuts down cleanly, and you're back in the IDE. That's the whole development loop, and it is identical on Windows and on Linux.

The generated server uses the Sparkle socket server, which behaves the same on both platforms and needs no administrative setup. If you'd rather run on Http.sys for a high-load Windows server, the generated code has the lines for it ready to uncomment.


Deploy on Windows: one command

There's no service project to build. The executable you have just been debugging installs itself. From an elevated command prompt:

MyServer.exe --install

That's it. Your server is now a Windows service, set to start automatically at boot, and you manage it like any other:

sc start MyServer
sc stop MyServer

MyServer.exe --uninstall removes it. When it runs as a service there is no console to write to, so the host writes the log lines to a file next to the executable (or wherever you point it with --log=).

Run the executable with --help and it lists everything it accepts. No hand-written service registration, no sc create command line to get right, no separate installer step.


Deploy on Linux: one pipe

The same program writes its own systemd unit file:

./myserver --systemd-unit | sudo tee /etc/systemd/system/myserver.service
sudo useradd -r myserver
sudo systemctl daemon-reload
sudo systemctl enable --now myserver

Four lines, and your server starts at boot, runs under its own user account and restarts if it fails. Nothing else is installed on the machine: no Apache, no web server in front, just your binary. systemctl stop sends SIGTERM, which becomes the same graceful shutdown as Ctrl+C, and the log goes to the journal:

journalctl -u myserver -f


Everything in one process

A backend is rarely only HTTP. There's a nightly import, a queue to poll, a scheduler - things that used to justify yet another project and another service to install and monitor. The host runs them alongside your servers, in the same process, with the same lifetime:

begin
  CreateHost
    .ServiceOptions('SalesApi', 'Sales API Server', 'REST API for the sales system')
    .Add(TApiServerModule)
    .Add(TReportsServerModule)
    .Add(TNightlyJobsWorker)
    .Run;
end.

CreateHost takes any number of data modules and background workers, starts them in order and stops them in reverse. ServiceOptions is the name, display name and description the service gets when you install it. And if something fails to start - a port already taken, a database that isn't there - whatever is running is stopped, the error is logged, and the process exits with code 1, so the failure is visible to systemd, to the Service Control Manager and to your deployment script instead of looking like a normal shutdown.

A worker is that job runner or scheduler: a class with a loop of its own. Derive from THostedWorker, override Execute, and the host gives it a thread:

type
  TNightlyJobsWorker = class(THostedWorker)
  protected
    procedure Execute(const AStop: IStopToken); override;
  end;

procedure TNightlyJobsWorker.Execute(const AStop: IStopToken);
begin
  repeat
    RunPendingJobs(AStop);
  until AStop.WaitFor(60000);
end;

The loop waits on the IStopToken instead of calling Sleep, and that's what makes stopping instant: a service stop, a systemctl stop or a Ctrl+C ends the wait immediately, wherever in the minute it happened to be. Pass the token into long-running work and it can give up halfway instead of holding up the shutdown.

Still one project, still one executable.


Already have a server? It's a small change

Existing projects keep working: the previous wizards are still in the gallery under TMS BIZ (Deprecated), and the Sparkle.App units still compile.

But moving to the host is usually a few lines. A server generated by the old wizards, for instance, has a Server unit with StartServer and StopServer procedures shared by its VCL, service and console projects. Keep that unit exactly as it is, give it a one-file console project, and the other projects are no longer needed:

uses
  Sparkle.Host,
  Sparkle.Host.Shutdown,
  Server in 'Server.pas';

begin
  RunHost(
    procedure
    begin
      StartServer;
      try
        WaitForShutdown;
      finally
        StopServer;
      end;
    end);
end.

Your server code doesn't change. WaitForShutdown simply replaces the ReadLn you had in the console version - and that single line is what tells the host the server is up, which is what a Windows service has to report to the Service Control Manager and what systemd wants to know.

The migration guide covers this and the other starting points, including hand-written TService projects.


Conclusion

Deployment is the part of a server project that has the least to do with your application and causes the most trouble. The new hosting layer takes it off your hands:

  • One project and one executable for development, Windows services and Linux daemons.
  • F9 to run, Ctrl+C to stop, with the same code and the same behavior you ship.
  • Deployment built into the binary: --install on Windows, --systemd-unit on Linux.
  • Correct service behavior for free - graceful shutdown, start and stop reporting, meaningful exit codes.
  • Wizards for Sparkle, XData and RemoteDB that create this shape of project for you.
  • Existing servers keep working, and move over with almost no code change.

A complete example - an HTTP server and a background worker in one process - ships in the Sparkle distribution under demos/host.

To learn more, read the Hosting chapter in the Sparkle documentation, the getting-started guides for the XData Server wizard and the RemoteDB Server wizard, and the what's new list. And visit the product pages of TMS Sparkle, TMS XData and TMS RemoteDB, all part of TMS BIZ.



Wagner Landgraf




This blog post has not received any comments yet.



Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.



All Blog Posts  |  Next Post  |  Previous Post