[HN Gopher] Nginx Unit - Universal web app server
       ___________________________________________________________________
        
       Nginx Unit - Universal web app server
        
       Author : promiseofbeans
       Score  : 516 points
       Date   : 2023-09-10 08:10 UTC (14 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | jchook wrote:
       | Nginx Unit + PHP seems to handedly out-perform Nginx + php-
       | fpm[1][2][3].
       | 
       | Also, Docker environments running PHP via Nginx Unit will no
       | longer need separate containers for http + fpm, as it works
       | similar to Apache's mod_php.
       | 
       | 1. https://habr.com/en/articles/646397/
       | 
       | 2. https://medium.com/@le_moment_it/nginx-unit-discover-and-
       | ben...
       | 
       | 3.
       | https://github.com/nginx/unit/issues/6#issuecomment-38407362...
        
         | tiffanyh wrote:
         | Two questions:
         | 
         | 1. Does Unit + PHP doing the typical "initialize & teardown"
         | that PHP is know for?
         | 
         | Or is Unit persisting the initialization/setup, hence why it's
         | achieving the way faster results?
         | 
         | 2. How is Unit/PHP architecturally different than NGX-PHP (an
         | event loop)?
         | 
         | https://github.com/rryqszq4/ngx-php
        
           | Dachande663 wrote:
           | PHP's had an opcache and jit for several versions now.
           | Combined with preload options, this means startup time is
           | negligible.
        
         | conradfr wrote:
         | You don't need separate containers to run nginx and php-fpm.
        
           | kobalsky wrote:
           | does it involve running supervisor inside the container?
           | that's fine but it's definitely not kosher.
           | 
           | in my experience those are the ones that kick and scream when
           | they don't start as root.
        
             | conradfr wrote:
             | php-fpm & nginx -g "daemon off;"
        
             | [deleted]
        
           | wongarsu wrote:
           | Are you implying that docker containers don't run a unikernel
           | that can only run one application at a time? /s
        
           | martinsnow wrote:
           | I too would like to see a properly configured php+php-fpm
           | container benchmark. There's a lot of overhead when you link
           | by http instead of a unix socket, in the same container.
        
             | e12e wrote:
             | Technically, you should be able to share a domain socket
             | via a shared volume too (between an "app" and "web"
             | container) - as with postgres:
             | 
             | https://github.com/sameersbn/docker-
             | postgresql/issues/30#iss...
             | 
             | Curious about benchmarks and tests of TCP vs Unix domain
             | sockets between docker containers.
        
         | ComputerGuru wrote:
         | Those benchmark outcomes are ridiculously in nginx-unit's favor
         | over php-fpm, way more than I would have believed was possible.
         | What is php-fpm doing architecturally that is so different to
         | warrant such poor relative performance?
        
           | pbowyer wrote:
           | You see similar performance improvements in bencharmarks of
           | Litespeed & PHP LSAPI [1].
           | 
           | Unfortunately said benchmarks are usually done by the
           | software company making said webserver, so have to be taken
           | with a pinch of salt.
           | 
           | 1. https://www.litespeedtech.com/open-source/litespeed-
           | sapi/php
        
       | jiggawatts wrote:
       | This seems a lot more like how IIS works, unless I'm missing
       | something?
       | 
       | As an aside: it's always curious to see how the programming world
       | has splintered into cliques that no longer hang out together.
       | 
       | NGINX Unit is a "Universal" web server without support for C++,
       | Rust, or ASP.NET!
       | 
       | But _PERL_ is supported, like the 1990s Linux cgi-bin world never
       | went away.
        
         | coldtea wrote:
         | In Linux where this is primarily expected to be used ASP.NET is
         | extremely rare, as is C++ for web stuff, and Rust has a
         | vanishingly small web presence still. Lots of people use Perl
         | still though, if not for anything else, for legacy stuff.
         | 
         | So might as well ask why it doesn't support Delphi.
        
           | jddj wrote:
           | Do you have data for your asp.net Linux claim? Feels
           | anachronistic.
        
             | pseudosavant wrote:
             | This is anecdata, but my experience with modern ASP.NET
             | teams these days is that while production may still be
             | running Windows, the dev teams are a mix of Mac, Windows,
             | and Linux/WSL. The allure of the M2 MBP is too great when
             | the Windows option is an expensive but otherwise
             | forgettable Dell corporate laptop.
        
           | ComputerGuru wrote:
           | I can't say I know the percentage of Linux servers running
           | ASP.NET Core but the percentage of ASP.NET Core apps running
           | on Linux is no longer negligible. Bing runs it on Linux,
           | iirc.
           | 
           | This isn't the dark days of .NET Framework anymore.
        
           | mst wrote:
           | And PSGI is actually really rather good (for sync stuff, I
           | dislike the way it handles async/websockets and would tend to
           | use Mojolicious for that) and generally used to deploy OO MVC
           | style apps.
           | 
           | The Perl ecosystem has come a long way from CGI scripts just
           | like everybody else has.
        
         | crvdgc wrote:
         | Don't know about ASP.NET, but one of the supported languages is
         | assembly, so I'd presume C++ and Rust can easily add a wrapper
         | around that.
         | 
         | > Binary-compiled languages in general: using the embedded
         | libunit library.
         | 
         | From https://unit.nginx.org/keyfeatures/#supported-app-
         | languages
        
           | tyingq wrote:
           | Reads like it's not real straightforward or well documented
           | for rust:
           | 
           | https://github.com/nginx/unit/issues/738
        
         | mbreese wrote:
         | And Go apps require recompiling them to use their version of
         | the http. ListenAndServe function. I'm not sure if this counts
         | either...
         | 
         | https://unit.nginx.org/configuration/#updating-go-apps
        
           | sethammons wrote:
           | And requires you to enable CGO, which I avoid at all costs if
           | I can
        
       | mindwok wrote:
       | Well I commend the Nginx team for trying something new and
       | launching this, even though I'm not sure what I would use it for
       | personally.
       | 
       | Slightly tangental, but it always irks me when I see these kinds
       | of responses in JSON:                 {           "success":
       | "Reconfiguration done."       }
       | 
       | Really this should be something like "result": "success". Using
       | "success" as a key name tells me nothing about the data it's
       | representing.
        
         | giancarlostoro wrote:
         | I rather have:
         | 
         | { "responseMessage": "Reconfiguration done.", "responseCode":
         | 133, "isSuccessful": true }
         | 
         | This way you can map to a custom message on your front-end
         | based on the responseCode whether its successful or not, you
         | can fallback to the response message if no mapping is found,
         | and you can easily check if the transaction was successful or
         | not.
        
         | dan-robertson wrote:
         | I think this is trying to be a discriminated union type[1] in
         | json. One way to do it is to have a 'tag' field that says "ok"
         | or "err" or whatever and then other fields for the rest of the
         | data (or an array with the tag in the first slot). Another is
         | to have one field whose name is the tag and whose value is
         | everything else (which is what happened here).
         | 
         | [1] eg one where the possible values are either Err(<some
         | error>) or Ok(<some result>), and the data inside could be more
         | complex types instead of just strings
        
         | tecleandor wrote:
         | That's like when I see APIs that give you responses with a 200
         | code, but then in the message there's a backend error.
        
           | smashed wrote:
           | Some people treat http as a pure transport/network layer and
           | the actual body as the app-level layer.
           | 
           | In such an approach, any http-level error would be a network,
           | server maintenance or other unknown/fatal error type of
           | things.
           | 
           | App errors are encoded in the response body in a app-defined
           | way.
           | 
           | It's not a completely bogus way of handling things, as long
           | as it is perfectly consistent throughout the project and
           | properly documented, which is rarely the case.
        
           | FridgeSeal wrote:
           | Looks pointedly at graphQL projects.
        
           | wruza wrote:
           | The opposite of that is people who try to loosely project the
           | variety of error modes their app has to http status codes.
           | You then print out a table of http code <-> actual meaning
           | and try to outline ranges and messages you want to handle
           | differently. On top of that, their backend may be down and
           | you have to deal with bare reverse proxy statuses as well,
           | which adds another dimension to that mess.
        
         | tanepiper wrote:
         | Regarding use case. We have a bunch of frontend apps that are
         | integrations to one of our external platforms - they get hosted
         | in an iframe on there.
         | 
         | To simplify the builds we have a expressjs app that is a simple
         | static server and proxy for API requests.
         | 
         | I can see something like this easily replace that - all we need
         | is static files and a /API* endpoint to proxy - avoiding cors
         | issues.
        
         | 9dev wrote:
         | Heh. My pet peeve are useless success fields. A 200 response
         | tells you it was successful already. We can talk about a
         | ,,message" field or something if you intend to display it to a
         | user, but even that should be implicit by being a response to a
         | specific request. If I send a POST /configuration, is there
         | really something new to be gained from that message that the
         | client cannot figure out on its own? Wouldn't, perhaps, a 201
         | No Content suffice?
        
           | eterps wrote:
           | Eric S. Raymond, in his guide "The Art of Unix Programming",
           | mentions this principle as Rule of Silence: "When a program
           | has nothing surprising to say, it should say nothing."
           | 
           | His statement probably related to command line applications,
           | but it makes sense for a lot of cases.
        
             | pydry wrote:
             | It sounds smart but what counts as surprising is entirely
             | context dependent and most programs won't be aware of your
             | context.
             | 
             | E.g. a command line app where you put a subtly wrong switch
             | in that does exactly what it thought you wanted and prints
             | nothing while outputting a 0 exit code is dangerous.
        
               | couchand wrote:
               | It's about context, of course, but it's not really the
               | program's responsibility to know the user's context. It's
               | the user's responsibility to understand their own
               | context.
               | 
               | A tool should have sufficient interlocks to ensure safety
               | when not engaged, but no more.
        
               | loloquwowndueo wrote:
               | What, like rm -rf ?
        
               | 9dev wrote:
               | I follow your point, however it actually supports mine:
               | An HTTP response isn't intended to be the output of a
               | command line app. A command line app should take the
               | response from the lower-level HTTP communication and
               | articulate whatever appropriate message to the user,
               | depending on the verbosity level for example. Talking in
               | OSI terms - this is a presentation layer problem, not a
               | link layer one.
        
             | laserDinosaur wrote:
             | "When a program has nothing surprising to say, it should
             | say nothing."
             | 
             | Which sounds good in theory, until you run into issues like
             | this: https://www.youtube.com/watch?v=tLdRBsuvVKc
             | 
             | TLDW: Most of the Gitlabs outage where they deleted the
             | primary prod and backup prod database could have been
             | solved if they just waited for the command line op to
             | complete - but because of a lack of feedback they thought
             | it had frozen, leading to several other plans which made
             | the situation worse.
        
         | asmor wrote:
         | Unit has been around for half a decade. It feels like an
         | evolution to something like Phusion Passenger, but it's not
         | quite cloud-native. A lot of the documentation is tailored to
         | installing directly on a server and some essentials (i.e.
         | prometheus metrics) are missing.
         | 
         | I briefly evaluated it for bringing a PHP team into our
         | Kubernetes cluster, but then ended up writing a bit of Go code
         | to proxy into a real nginx+fcgi while adding a syslog sink (so
         | PHP could log to stdout/stderr) and our standard prometheus
         | metrics on the Go http server.
        
           | fideloper wrote:
           | wait, did that continue to use php-fpm? if not I want details
           | plz! :D (especially around the logging sync, unless it's just
           | php-fpm configured to collect child process output)
        
             | asmor wrote:
             | Yes, it did, not too much magic going on, the Go proxy also
             | was a replacement for supervisord so it started nginx and
             | fpm, reaped zombies and pulled all the important logs into
             | stdout/stderr. The most remarkable thing was how this
             | revitalized a team that wrote boring PHP software limited
             | to deploying via FTP on PHP 5.
             | 
             | They ended up really getting into stripping out every piece
             | of PHP they didn't use because the image built PHP from
             | scratch, eventually took over maintenance of the go piece
             | and ported a bunch of their apps to Symphony. I was in a
             | platform engineering team and they were one of the few
             | teams to really torture test every feature we ever shipped
             | to the point they'd report edge cases or a bug to us every
             | other week or so.
             | 
             | As for the logging question, we configured Symphony to log
             | to syslog, which was provided by the Go daemon via unix
             | socket.
        
               | fideloper wrote:
               | awesome, thank you for the details!
        
         | afavour wrote:
         | If I were designing it I'd probably have a result field too, or
         | even a success boolean. But I don't hate what they're doing
         | here: the presence of the key is the Boolean value, the value
         | is the description. A two for one.
        
           | wruza wrote:
           | An economy not worth investing in in my book. It's always
           | more clear to separate success and result. Sometimes, you'll
           | have no result, so there will be some parasitic (or truthy)
           | value like null or true. And when you check success-named
           | result deeper/further in code, it looks like you're indexing
           | into a boolean. Trying to make it clear on-site creates
           | miniprotocols not worth remembering. Worst case scenario is
           | {success:x, error:y}, x and y being 4 combinations of null
           | and non-null, where you aren't even sure what happened and
           | may swallow a false positive with an optimistic check. Also,
           | since undefined is not in json standard, the existence
           | becomes ephemeral in languages where that's distinct from
           | just being undefined. You may think that returning undefined
           | from a wrapped worker is okay, but it results in "error
           | encountered: undefined" down the line due to {} payload. It's
           | one of the things that make programming harder for no good
           | reason.
        
         | chrismorgan wrote:
         | In this _particular_ example, it's actually a whole lot worse
         | than that, by spec at least. Excerpt from their OpenAPI schema
         | <https://github.com/nginx/unit/blob/7dd5ad93a4c147b086a8d82ec..
         | .>:                   jsonSuccessMessage:           type:
         | object           description: "JSON message on success."
         | additionalProperties:             type: string
         | jsonErrorMessage:           type: object           description:
         | "JSON message on error."           additionalProperties:
         | type: string
         | 
         | Yes, this is as bad as it looks: _"success" isn't even part of
         | the schema_. It's in the _examples_ , but not the actual schema
         | definition. The way success or failure is _actually_ indicated
         | is by HTTP status codes. 200 is success, 400 /404/500 is error.
         | So, they seem to set very bad precedent in at least one of
         | their response and their schema.
         | 
         | But I was expecting it to be something like {"success": string}
         | | {"error": string}, which is frankly a perfectly reasonable
         | way of doing things: an untagged, but still unambiguous, union.
         | It can make for quite pleasant code, too: `if response.success`
         | and such.
         | 
         | You're looking for a tagged union, something like {"result":
         | "success" | "error", "message": string} (or alternatively like
         | {"result": "success", "message": string} | {"result": "error",
         | "code": string, ...}), which is also a perfectly reasonable way
         | of doing things. In some ways it can be viewed as more
         | principled, as it _more formally_ allows you to check the tag.
         | 
         | Really, the two approaches are much of a muchness. They each
         | have their strengths and their weaknesses. But if general
         | status is being done at the HTTP layer, I'd prefer {"message":
         | "Reconfiguration done."}, or... well, actually, just a 204 No
         | Content response, and no JSON or body at all.
         | 
         | (On the terms untagged and tagged unions as I'm using them:
         | they're necessarily a bit different from the concepts as
         | exposed in languages like C, since you're dealing with a model
         | built on objects rather than bytes, but they're still
         | reasonable descriptions of them. In Serde's classifications,
         | they'd be the untagged and internally tagged enum
         | representations <https://serde.rs/enum-representations.html>.)
        
           | thibaut_barrere wrote:
           | > Yes, this is as bad as it looks: "success" isn't even part
           | of the schema. It's in the examples, but not the actual
           | schema definition.
           | 
           | Having gone through a pretty heavy overhaul of an "OpenAPI"
           | (full code in Elixir at https://github.com/etalab/transport-
           | site/pull/3351), I stumbled on that exact type of problem!
           | 
           | At the scale of nginx, having automatic verification that the
           | examples (and the output of the API in general) match the
           | specification would be great.
           | 
           | At our scale, here is what really helped me go through the
           | rework (and ensure we do not regress too easily):
           | 
           | - setting additionalProperties to "false" to detect key field
           | "rot"
           | 
           | - using "required: [x,y,z]" on everything, and by default
           | specify "all the property keys", with an opt-out (so that
           | each time a developer adds a field later, it is considered
           | mandatory, unless otherwise specified)
           | 
           | - use tooling during the tests: "assert_schema" (with
           | OpenAPISpex) to ensure our API endpoints responses pass the
           | spec (additionalProperties: false helps ensure we get an
           | exception in case of key field rot, again!)
           | 
           | - even more useful: crawl our production most important
           | endpoints and tweak the spec until everything is green (an
           | example of useful use of Task.async_stream in Elixir, by the
           | way) https://github.com/etalab/transport-
           | site/pull/3351/files#dif...
           | 
           | It can be super frustrating for users to live with the
           | uncertainty of the response of an API for sure, and I was
           | happy to discover the Elixir tooling (OpenAPISpex in
           | particular) worked so nicely once I understood what I had to
           | do.
        
             | tough wrote:
             | I recently found this open source postman typed
             | alternative, hadn't had time to really try it yet but the
             | types stuff should help here no?
             | 
             | https://recipeui.com/
        
               | thibaut_barrere wrote:
               | The type support in OpenAPI is fairly good (enough for
               | us), and using a tool directly built on our stack allows
               | nice things.
               | 
               | Thanks for the link though!
        
           | usrusr wrote:
           | Looks like the openAPI is a little rough around the redundant
           | status fillers? Wouldn't be the first one I guess.
           | 
           | Putting the redundancy with 200 or not 200 aside, I sense a
           | certain aesthetic quality in the
           | {"success":string}|{"error":string} approach. Namely in how
           | it adheres to keeping the schematic stuff on left side of the
           | colon.
        
           | Nullabillity wrote:
           | By Serde's classification, `success: {}` is a textbook
           | _externally tagged_ representation.
        
             | chrismorgan wrote:
             | If there was always only one top-level field and everything
             | else was inside that, it could be externally tagged, but
             | it's definitely just untagged here, because
             | #/components/examples/errorInvalidJson/value (line 4397)
             | shows an error body with multiple top-level fields:
             | {"error":"Invalid JSON.","detail":"...","location":{"offset
             | ":0,"line":1,"column":0}}. Externally tagged would be
             | {"error":{"message":"Invalid
             | JSON.","detail":"...","location":{...}}}.
        
             | mindwok wrote:
             | I wasn't familiar with Serde's description of this so that
             | was a good read, thanks.
             | 
             | Although I still think this is a bad example of an
             | externally tagged representation. In the Serde example they
             | have the key as "Request" then what follows is the request
             | object. In this example, the "success" key is followed by
             | an arbitrary string message, which isn't obvious at all.
        
               | Nullabillity wrote:
               | The contents of each variant don't really matter here.
               | The tag represents the variant, not the type contained
               | inside of it. The Serde equivalent here would be:
               | #[derive(Serialize, Deserialize)]
               | #[serde(rename_all = "camelCase")]         pub enum
               | Response {           Success(serde_json::Value),
               | Error(serde_json::Value),         }
        
         | [deleted]
        
         | maxloh wrote:
         | Why including that in the response body? 200 status code states
         | everything.
        
         | revskill wrote:
         | Should it be
         | 
         | { status: 200, body: { "message": "Reconfiguration done." } }
         | 
         | ?
        
       | KronisLV wrote:
       | Hey, this is pretty cool to see!
       | 
       | Here's a page with the features:
       | https://unit.nginx.org/keyfeatures/
       | 
       | The languages supported, if anyone is curious:
       | Binary-compiled languages in general: using the embedded libunit
       | library.       Go: by overriding the http module.
       | JavaScript (Node.js): by automatically overloading the http and
       | websocket modules.       Java: by using the Servlet Specification
       | 3.1 and WebSocket APIs.       Perl: by using PSGI.       PHP: by
       | using a custom SAPI module.       Python: by using WSGI or ASGI
       | with WebSocket support.       Ruby: by using the Rack API.
       | WebAssembly: by using Wasmtime.
       | 
       | I'm currently using Apache as a reverse proxy for most apps and
       | sometimes PHP-FPM when I need to run software with PHP, which
       | works well for my personal needs (mod_md and mod_auth_openidc are
       | pretty cool), but it's cool to see something similarly
       | interesting to OpenResty coming along as well, too!
        
       | vcryan wrote:
       | For a NodeJS app, could you remove something like nodemon by
       | using this? That could make this appealing.
        
         | amiga-workbench wrote:
         | You can replace nodemon/PM2 with a systemd unit file.
        
       | Dachande663 wrote:
       | Tested this with a moderately complex PHP Laravel app and got a
       | 40% speed improvement. Very useful to be able to run multiple
       | apps each using different lang runtimes/versions without needing
       | separate docker containers.
        
         | dieulot wrote:
         | 40% improvement versus what? A docker container for nginx and
         | another one for PHP?
        
           | Dachande663 wrote:
           | Compared to nginx and PHP-FPM installed on a host machine.
           | All running opcache, JIT etc. Was pleasantly surprised. Note
           | this is up to 40%, not all apps we've run have seen same
           | improvement.
        
         | [deleted]
        
         | sidcool wrote:
         | What were you using earlier?
        
         | quickthrower2 wrote:
         | Sounds like a blog post in the making
        
         | husarcik wrote:
         | How does this speed improvement compare to Laravel octane with
         | swoole? Curious if I can switch!
        
           | Dachande663 wrote:
           | We tried roadrunner (similar thing), but couldn't do it
           | without rewriting certain bits of the app (anything with
           | globals basically) whereas unit was a straight swap. My guess
           | would be, if you're properly using the advantages swoole can
           | bring (like shared memory cache etc), it will outperform
           | unit.
        
       | talboren wrote:
       | YAWS - yet another web server
        
       | e12e wrote:
       | 2017?
       | 
       | https://youtu.be/I4IWEz2lBWU
        
       | akokanka wrote:
       | Yeah welcome to NgineX universe, hence I still prefer Apache
       | httpd
        
       | tomjen3 wrote:
       | This is an interesting idea, but I feel like it is a big
       | oversight not to have build in support for automatically getting
       | certificates.
       | 
       | Instead the docs have you do something manual with certbot (a
       | complete nono if you believe in automatic SSL and are using
       | docker images that don't persist data, as Docker is meant to be
       | used).
       | 
       | The only reason I have SSL on my domain in the first place is
       | that my host offers a simple setup that runs automatically.
        
         | _joel wrote:
         | > using docker images that don't persist data, as Docker is
         | meant to be used
         | 
         | docker volumes entered the chat
        
           | tomjen3 wrote:
           | Which means now you can't do a clean deploy of a new version,
           | you can't be sure that what works on one machine is the same
           | as what works on another etc.
        
       | ComputerGuru wrote:
       | Can you feasibly run this behind nginx (either over http in a
       | regular reverse proxy setup or with sort of more clever shared
       | memory forwarding situation?) so you could, eg use nginx-unit
       | instead of php-fpm but keep everything (ssl termination, rate
       | limiting, etc) centralized behind your core nginx load balancer?
       | 
       | Edit: yes, it's as straightforward as it sounds:
       | https://unit.nginx.org/howto/integration/
        
       | ojintoad wrote:
       | excited for .NET support so I can write NUnit tests verifying my
       | NGINX Unit hosted software
        
       | CodeCompost wrote:
       | Looks like this is more focused on ease of use rather than
       | bringing new features in.
        
       | DarkCrusader2 wrote:
       | > open source server project that works as a reverse proxy,
       | serves static assets, and runs applications in multiple
       | languages.
       | 
       | Isn't this what Nginx also does. How is Nginx Unit different
       | beyond configuration via JSON REST APIs? The Github readme or
       | their site is not very clear on this.
        
         | pacifika wrote:
         | Nginx doesn't talk to Python or php directly it's talks to the
         | application server.
        
       | stefantalpalaru wrote:
       | [dead]
        
       | dmarinus wrote:
       | this is a nice alternative for the apache php module (mod_php) if
       | you don't want to or can't use php-fpm.
        
       | yetanother-1 wrote:
       | Would you prefer this over Traefik for a simple docker-compose
       | setup?
        
         | Grimburger wrote:
         | > a simple docker-compose
         | 
         | Abstraction at it's finest.
        
           | 9dev wrote:
           | What strikes you as difficult with compose files? In fact, I
           | would say it's the most concise format to describe a desired
           | state of running applications currently available.
        
             | Grimburger wrote:
             | Nothing is difficult about them, compose is easy enough
             | that you can grok it in a day.
             | 
             | You saw the lament in my comment though and that speaks a
             | deeper truth.
             | 
             | If WSL took off quick enough we could have had lxc as the
             | main player rather than the bastardisation of lxc that is
             | docker.com
             | 
             | For an open source project they make it incredibly hard to
             | access what are essentially text files for containerisation
             | setups on windows or mac?
             | 
             | https://github.com/docker/hub-feedback/issues/1103
        
               | 9dev wrote:
               | Compose is an open source project entirely separate to
               | hub, though. The compose file specification is versioned
               | separately, and will outlive Docker, probably. So I don't
               | quite get your criticism?
        
               | Grimburger wrote:
               | How are you using compose without docker? They are joined
               | at the hip, criticism of one is criticism of the other.
               | 
               | The days of cheap money are over, it's inevitable that
               | certain SaaS companies will start tightening the screws
               | on their users to match the returns they can get with
               | cash in a bank. I just wish lxc (which docker was built
               | off) got a chance to gain traction. It's miles ahead in
               | DX and sure they serve different functionalities but can
               | be used the same and the network effects can't be
               | understated.
        
           | notpushkin wrote:
           | Unironically this. After trying Kubernetes, Terraform,
           | Ansible, and various proprietary PaaS config formats, I've
           | grown to love Compose files' simplicity.
        
             | segfaltnh wrote:
             | Yes, when compared to extreme complexity, compose is
             | simpler. But it's hardly simple.
        
               | notpushkin wrote:
               | I'll happily try anything simpler than that if you give
               | me an idea what that might be.
               | 
               | Not trying to be salty or anything - I really think
               | Compose hits the sweet spot of abstraction which is less
               | complex than both the monstrosities I listed and the _ad
               | hoc Bash scripts copying code over SSH and restarting
               | services_ approach (so, the other extremity of
               | declarative v. imperative).
        
         | ljm wrote:
         | I didn't think I would see traefik and simple in the same
         | sentence.
        
       | fideloper wrote:
       | I was just playing with this the other day.
       | 
       | Trying to find context on what it was, I saw it's been on HN a
       | few times (mostly to the sound of people asking the same
       | question).
       | 
       | Since I mostly do php/laravel, I was pleasantly surprised that it
       | let me remove php-fpm from my stack - extremely nice when putting
       | PHP apps in a container.
       | 
       | I personally have come to dislike PHP-FPM's max_children and
       | similar settings, whose low defaults come as an annoying surprise
       | when you suddenly get gateway errors before your server is even
       | overloaded. (and having to add nginx AND php-fpm "layers" into a
       | container to run PHP apps is all sorts of annoying).
       | 
       | Unit doesn't have as many features as Nginx so I suspect it's
       | best used when there's another fully-featured http layer in front
       | of Unit (to more easily handle TLS, protecting dot files, gzip,
       | cache headers, and the like).
       | 
       | Overall i'm excited to use it when making images for php apps.
        
       | tandav wrote:
       | If you need just reverse proxy, Caddyfile syntax looks simpler:
       | myapp.company.com {             reverse_proxy localhost:3000
       | }
        
         | logeist wrote:
         | Came here to say Caddy still seems like the way to go for most
         | use cases. Although I don't use it in any 'prod' environments
         | it checks all the boxes.
        
       | mirzap wrote:
       | What is the difference between Nginx and Nginx Unit? Benefits?
        
         | jalk wrote:
         | Nginx-unit is reconfigurable using REST:
         | https://unit.nginx.org/controlapi/
        
       | usrusr wrote:
       | So much JSON... is that the real thing, as in comments are the
       | gateway to hell, or is it actually some practical JSON superset
       | like JSON5?
       | 
       | I almost see myself jury-rigging some bespoke filesystem on-ramp
       | to that REST configuration interface. Likely with at least half a
       | dozen scary security compromises.
       | 
       | Then on the other hand I guess they've never been afraid of
       | getting called opinionated and that's certainly much better than
       | trying to be everything, for everyone, at the same time.
        
         | gjvc wrote:
         | JSON without comments is hell, forcing one to 1) convert JSON
         | configuration to YAML. 2) add comments to YAML and admire it.
         | 3) convert YAML to JSON for sending to the server.
        
           | coldtea wrote:
           | YAML is an order of magnitude more hell...
        
             | sethammons wrote:
             | I literally cannot count the number of times significant
             | whitespace has broken a yaml file I've edited followed by a
             | completely useless error on line 1. I don't have this
             | problem in python. I loath yaml.
        
             | johnchristopher wrote:
             | But TOML is worse, right ? I can't write nor read TOML to
             | save my life... :(
        
               | rascul wrote:
               | TOML is much simpler in general but it does have its
               | warts. It's basically just key = "value" pairs in
               | [sections] for the simple stuff.
        
               | coldtea wrote:
               | I think it's saner, but still has some bizarre design
               | choices
        
           | usrusr wrote:
           | Fortunately it's only forcing one to convert to a JSON
           | variant that does not forbid comments (starting as simple as
           | wrapping the entire thing in console.log JSON.stringify).
           | I'll happily leave the YAML authoring to YAML inventors,
           | supposedly it makes them happy.
        
         | otterley wrote:
         | I like to think of JSON as a low-level configuration language
         | that most people should generate from a higher-level language
         | such as Python or JSONnet -- sort of like assembly is to C.
         | JSON has lots of things to like about it, but I don't recommend
         | generating it manually for most people for files larger than a
         | few lines.
        
       | [deleted]
        
       | gibmeat wrote:
       | Why the obsession (it seems to be the prominent point in the
       | readme) with configuration via API? How often do you need to add
       | php support on the fly? I want to configure my app server via
       | files so it just starts up in the state that I expect. What am I
       | missing?
        
         | gabereiser wrote:
         | Why? To scale infra of my offering to my customers. They need
         | it too. I'd like for my remaining customers to not suffer
         | downtime. I'd like to use the existing infra I have without
         | spinning up new ones. I'd like to offer a dashboard so my
         | customer can configure their host.
        
         | fragmede wrote:
         | > How often do you need to add php support on the fly?
         | 
         | Restarting the binary means you'll lose requests while it's
         | restarting, so adding php (or whatever) support on the fly is
         | what you need when running a system where losing those requests
         | is material. Which it won't be for most people, but for, eg,
         | Google (who don't use Nginx), losing those requests is a
         | problem.
        
           | 22c wrote:
           | Although it doesn't directly go against what you're saying,
           | many Unix daemons have supported HUP signals for decades
           | which can achieve the same outcome. No need to configure via
           | API, just change the configuration on disk and send HUP.
           | 
           | I suppose arguably that becomes a bit trickier for
           | containers, so perhaps that's why you'd want to configure via
           | an API?
        
             | hamandcheese wrote:
             | To me this is identical - mutable state is mutable state,
             | whether it starts on disk first makes no difference.
        
           | hamandcheese wrote:
           | Any half decent containerized setup should support zero-
           | downtime deploys. Usually it involves bringing up new
           | containers and signaling the existing containers to begin
           | draining connections.
           | 
           | For most workloads it should be entirely possible to deploy a
           | new stateless config and not need to resort to using mutable
           | state for critical infrastructure.
           | 
           | If you have long-lived, stateful connections (perhaps for
           | live streams) then I can see why re-configuring in place
           | would be desirable, but in my experience that's pretty rare.
        
           | xorcist wrote:
           | Neither Apache nor nginx require a restart to add php
           | support, and neither will lose requests under normal
           | operation. They will however parse the complete config on a
           | reload operation. On huge configurations this is noticeable.
        
         | didip wrote:
         | For webapp, maybe not very valuable.
         | 
         | But for API gateway or any kind of sidecars? Very valuable.
        
         | ramesh31 wrote:
         | >Why the obsession (it seems to be the prominent point in the
         | readme) with configuration via API?
         | 
         | Infrastructure As Code (in all its forms,
         | chef/puppet/ansible/tfe etc.) is the standard for all
         | enterprise cloud setups these days. It makes sense to support
         | that as a first class feature.
        
           | shawabawa3 wrote:
           | but this is the opposite of infrastructure-as-code
           | 
           | This is infrastructure-as-state
           | 
           | You have to manage automatically loading configs and keeping
           | in sync with what you have in code, instead of deploying with
           | a static configuration file
        
             | notnmeyer wrote:
             | it's not hard to imagine a use case where there are backend
             | configurations stored in a database somewhere and you want
             | to apply them. i'm picturing it as data vs static
             | configuration.
        
         | jen20 wrote:
         | Perhaps adding support for PHP on the fly is an extreme case,
         | but reconfiguring eg load balancer backends when new systems
         | come and go without having to render a config file and HUP (and
         | hope) is a typical case.
        
         | indymike wrote:
         | If the state you need at startup isn't the same as you need for
         | production, this could be incredibly useful. It also means that
         | you can save a lot of time starting and stopping containers for
         | many common configuration changes in production. There's a lot
         | more utility in this than just PHP.
        
         | sneak wrote:
         | This allows you to start up generic machines with no
         | configuration and customize them after boot from a remote host.
         | 
         | It's not so much "on the fly", as it is moving the long-term
         | config storage to a different system.
        
           | remram wrote:
           | I don't buy it. For this situation I would much rather have
           | the software load its (regular) configuration file from a
           | URL.
        
         | Ayesh wrote:
         | Probably the most common use case is SaaS providers that
         | support custom domain name for whatever the software it is. For
         | example, a site uptime monitoring service might offer a feature
         | to host a status page on a custom (sub)domain of the customer.
         | The SaaS now needs to programatically create virtual hosts on
         | demand, issue HTTPS certificates, run routine updates, etc.
         | 
         | An API and a web server with small segmented updates make this
         | so much easier. Compare this to Apache, that has to wait to
         | properly end existing connections before reloading, has a
         | config file parsing overhead, and probably does not scale that
         | well with several virtual hosts anyway. There are hardware/File
         | System level limitations as well.
        
       | ExoticPearTree wrote:
       | This seems to be a "universal" app server, like gunicorn is for
       | running Pyhthon stuff, php-fpm for PHP and so on.
       | 
       | It was first launched as closed source and the marketing
       | gibberish f5 came up with made it sound like make believe.
        
       | [deleted]
        
       | ghoshbishakh wrote:
       | This is really interesting. Wonder how does it compare to tomcat
       | or undertow for Java
        
       | ilovefood wrote:
       | This is really great! What I'm really look forward to trying is
       | the server side WebAssembly:
       | https://unit.nginx.org/configuration/#configuration-wasm.
       | 
       | Huge potential!
        
         | eterps wrote:
         | What specifically are you looking forward to?
        
           | ilovefood wrote:
           | I've been tinkering quite a bit with WasmCloud and WasmEdge
           | lately, for some data workloads (scraping, ingesting, apis
           | etc) mainly as an excuse to learn WebAssembly.
           | 
           | What I want to see for myself is how Unit compares to those
           | for this reduced scope (api's/services) and how fast I can be
           | productive with it. This could be a good starting point for a
           | lot of new applications, kind of an "airflow (the data
           | orchestration thing) on Webassembly".
           | 
           | So main points are performance (is it good enough) and
           | developer experience (is it easy to maintain/change).
           | 
           | Nothing really ground breaking, just trying out stuff.
        
         | drawfloat wrote:
         | What's the advantage of server side WebAssembly over just
         | running a Rust/whatever application?
        
           | pseudosavant wrote:
           | Security isolation too. I am using a WASM-based image
           | processing pipeline for handling user submitted images. Much
           | safer than trying to run a binary (Imagemagick, ffmpeg, etc)
           | written in an unsafe language. In my case, the WASM does not
           | have access to anything outside the sandbox.
        
             | imhoguy wrote:
             | Have you compared how much performance overhead has such
             | sandbox in comparison to native (Linux) binary? Especially
             | video processing. I just wonder if WASM app can even
             | benefit from SSE4 or AVX instructions.
        
           | politelemon wrote:
           | My limited understanding from various blog posts is that you
           | can still get the benefits of a container, but much faster
           | startup/runtimes. As in WASM provides performance very close
           | to native.
        
       | sideeffffect wrote:
       | It even supports Scala (Scala Native, that is)
       | 
       | https://github.com/lolgab/snunit
        
       | spoiler wrote:
       | I only glanced at the docs, but way they expect apps to integrate
       | with it seems a bit convoluted to me? It's not really a reverse
       | proxy at this
       | 
       | What would be wrong with just using HTTP/2, maybe with a few
       | custom headers sprinkled in for custom features?
        
         | TheHappyOddish wrote:
         | > It's not really a reverse proxy at this
         | 
         | In fact it's not one at all, nor does it claim to be. It's an
         | app server.
        
           | cxcorp wrote:
           | > NGINX Unit - universal web app server - a lightweight and
           | versatile open source server project that works as a reverse
           | proxy, serves static assets, and runs applications in
           | multiple languages.
           | 
           | https://github.com/nginx/unit
        
       | layer8 wrote:
       | They should make a page explaining how this differs from Nginx
       | and why it needs to be its own separate thing. I couldn't find
       | such a page.
        
         | bewuethr wrote:
         | Here maybe? https://www.nginx.com/products/nginx-unit/
        
           | slim wrote:
           | does not answer the question
        
             | bewuethr wrote:
             | I feel like the section "How Does NGINX Unit Compare to
             | NGINX Web Server?" does answer some aspects of it.
        
       | [deleted]
        
       | l5870uoo9y wrote:
       | Thought for a second that it included automatic TLS certificates
       | but that is still done manually with Certbot, which also isn't
       | that difficult.
        
         | danappelxx wrote:
         | Not that difficult, but it's still a separate dependency (with
         | python requirements). If the goal here is a Caddy competitor,
         | then IMO it's missing the mark in terms of "one stop shop".
         | What's the killer feature?
        
           | wolletd wrote:
           | On the python requirements: I've always been satisfied with
           | Bash implementations of ACME. I use dehydrated from the very
           | beginning (when it was still called letsencrypt.sh) and
           | lately started using acme.sh.
        
         | kijin wrote:
         | This thing will probably run in a bunch of VMs/containers
         | behind a load balancer, so it's actually better that it doesn't
         | try to obtain any certificates by default.
         | 
         | Even a small number of apps trying to get their own
         | certificates at the same time can exhaust the Let's Encrypt
         | quota for your domain, with serious consequences to your other
         | online properties.
        
           | smarkov wrote:
           | > Even a small number of apps trying to get their own
           | certificates at the same time can exhaust the Let's Encrypt
           | quota for your domain
           | 
           | I got curious about this and decided to look it up, they
           | actually have more restrictions than I expected[1]. Looks
           | like to play it on the safe side you might be better off
           | having a single server issuing certificates and distributing
           | them where needed as well as using wildcards as much as
           | possible. Interestingly, it looks like Google is doing just
           | that since their certificate covers a very wide range of
           | domains[2].
           | 
           | [1] https://letsencrypt.org/docs/rate-limits/
           | 
           | [2] https://www.sslchecker.com/sslchecker?su=b39e88e1c4d3efcd
           | b79...
        
         | [deleted]
        
       | smetj wrote:
       | Finally something exciting! If only this had event push
       | capabilities usefull for discoverability and announcement
       | patterns over various sinks http/nats/mqtt/amqp ...
        
       | vinay_ys wrote:
       | It is a great start and shows very good potential. Still need to
       | build support for io_uring, and all the sundry features in
       | mainline nginx, apache, haproxy etc.
        
       | lagniappe wrote:
       | I built something like this, but with much simpler syntax and
       | automatic https simply bc I'm not smart enough to reliably set up
       | server blocks and letsencrypt every time I route a new app.
       | 
       | AppServe takes care of all that, and even if I did know the
       | syntax by heart to do it in nginx, this is still faster.
       | 
       | https://github.com/donuts-are-good/appserve
        
         | Matl wrote:
         | Nice, that being said if the concern is complex config, there's
         | always Caddy[1].
         | 
         | 1 - https://github.com/caddyserver/caddy
        
           | lagniappe wrote:
           | AppServe gets compared to Caddy a lot, which is very
           | flattering, so thank you :) Caddy's good stuff!
        
       | Ranked6815 wrote:
       | I choose envoy
        
       | fulafel wrote:
       | There seems to be code in several languages - are the parts on
       | the front lines of untrusted input written in some memory safe
       | language?
        
       | whatever1 wrote:
       | So for a python django project this would replace gunicorn?
        
         | winrid wrote:
         | I would also like to know this.
         | 
         | Edit, yes: https://unit.nginx.org/howto/django/
        
       | [deleted]
        
       | abdellah123 wrote:
       | I'd love to see a performance benchmark for unit. Especially for
       | an nodejs/express app
        
       | donatj wrote:
       | I remember when they announced this a couple years ago I had no
       | idea why I would use it over standard nginx. That seems to still
       | be the case.
       | 
       | It's JSON controlled via Curl? And has a bunch of langue's built
       | in for some reason? The question becomes why?
        
         | pseudosavant wrote:
         | This is the question I keep asking myself as I read more. Why?
         | 
         | Only the NGINX team would think to "simplify" loading a
         | configuration file from the CLI by using curl to post it.
         | smh...
        
       | vanjajaja1 wrote:
       | I like it, has potential to push docker out of the application
       | layer
        
       | andrewstuart wrote:
       | I switched to caddy from nginx and didn't look back.
       | 
       | Auto SSL wildcards was enough.
        
         | tacker2000 wrote:
         | How is Caddys performance compared to nginx?
        
           | mholt wrote:
           | It's very good. In some cases better. For example Caddy can
           | handle tens of thousands of certs without problem whereas
           | nginx + Certbot would choke.
           | 
           | The difference is negligible to most people.
        
       | MrStonedOne wrote:
       | [dead]
        
       ___________________________________________________________________
       (page generated 2023-09-10 23:00 UTC)