 Hey, thanks for coming. All right. So, first off, show of hands, please. Anybody recognize this logo? Only the, well, not the younger folks. So this is a logo for NCSA Mosaic, one of the first graphical web browsers. This is from, I don't know, in-period web browser, 1990-something, you can still find this page. If you look at the web archive, it still renders just fine. And it comes from a time of the early days of the Internet. We're talking 92, 93, 94. And the Internet was a very different place. We didn't have search engines, and we didn't have web applications. We just had static web content, which was pretty great for them. And what you would do if you wanted to discover the Internet and everything that was on it, without a search engine, is that you'd use your web browser homepage. And if we click in on here a little bit, we'd have some resources to help you introduce you to cyberspace and keep track of its growth. And indeed, more than that, the Mosaic team ran this page called What's New on the Internet, and they'd update it every time something was new. Not every day, because not every day something happened, but they would curate the list, and you would go there and you'd find new stuff. A bit different to today when, you know, my favorite one of these is this 2 million Tinder swipes in a minute or 500 hours of content on YouTube, right? Content is just everywhere. These are mostly consumption metrics, not content creation metrics, but you get the idea. It was very different, and this whole thing changed when we were able to build web applications. So if we go back and look at 1st of April, 1994, the What's New page on the Internet, there's some interesting stuff like these. Let's see. Some Gopher mentions still, some interesting stuff, and at the top is Liam's web quiz. Before I wrote my first web application, currently I'm leading a team with Nginx, and I'm not doing that, I'm usually fixing the race car. But the reason I mention all of that is because the web changed completely when we started to be able to build applications and not just have static content. And that was 1994, when I was first writing my first web applications. The patchy didn't exist. It was 1995. Linux existed since 1991, so we had that. Patchy was 1995 first version. My SQL was also 95. PHP was also 95. And together, these amazing technologies gave us the LAMP stack. Anybody build web applications with a LAMP stack? Yeah, like this stuff was important. So 95 is when most of these technologies became available. It's like open source greatest hits, where the whole was greater than the sum of its parts, the things that it enabled. You had this stack that you knew how to use. People would help you. It was trusted. It was proven. And some of the biggest technologies and have been built on it, Wikipedia still runs on the LAMP stack. WordPress is LAMP stack. The first version of Facebook was LAMP stack. Slack is still LAMP stack, but it took 10 years for these other technologies to come into common use, and the term LAMP stack actually was... Yeah, it took 10 years. So 2005, we started to see O'Reilly in particular coined this phrase, did a bit of marketing around it, and then over the next decade is really when we saw that this stuff happened. So I'm sure your hands are going up somewhere along this decade timeline. And it's still good now, but things have changed a little bit. So it's interesting these technologies took like a decade to come to be a thing, and a decade is when they kind of rose to their peak. But it seems to me at least, having followed it since the beginning, that these timelines get shorter and shorter. And somewhere along this timeline, roughly the same kind of period is when Nginx came about. So sometimes you saw LAMP stack, sometimes you saw LAMP stack where Apache was swapped out for Nginx, and that typically was because there was a higher performance, higher concurrency requirements needed, and the earlier versions, at least if Apache didn't scale so well. In fact, the story of Nginx starts with this guy, Igor Sosoyev. He actually starts with him writing Apache modules. So he was working as an assist admin, right? SRE would be today's job description. He was working for a web portal company, like a Yahoo kind of thing. And the growth of the internet at that time, roundabout turn of the century, was really pushing systems over their limit. So he had this challenge. I want more servers, more users on a single server. And in fact, the challenge specifically he was trying to address was to get 10,000 concurrent connections on a single server, which really just wasn't possible. Hardware box, of course, wasn't possible at the time. He set out to achieve this, and he did it successfully. But before I go into how, my favorite analogy for what the web server has to do is it's like playing a game of chess with a lot of unknown and random behaving opponents. So a client arrives, they demand to play chess. They want a web page. They want to upload a file, whatever it is. The web server has to accept the move, deal with it, and respond with something else. It's within the rules. The client might send some more moves, play some more chess, or it might not. It might go away. We don't know if it's gone away. It just might stop talking to us. If the client's speaking HTTP version 2, then he'll send lots of moves at the same time, and we have to figure that out and send them all back. And when the client's finished playing, it won't tell us it's finished playing. It'll just stop. It may or may not close the connection. Just stuff stops happening. Could be where the client's not playing chess with us. He's playing chess with some back-end origin server, and we're in the middle being a proxy, so we could play one game of chess with the client, another game of chess with the back-end. Usually, the back-end is a lot worse at playing chess than we are. And ultimately, most of the time in this game of chess is spent waiting. There's an awful lot of waiting going on. And what the web servers of the previously would play chess like this, right? They would duplicate themselves for every client that turned up. So they'd have the board and the pieces and the rules of the game, and they'd make a copy every time there was a new request. It works really well, and at low levels of concurrency, it's really fast, because you have a dedicated copy of your program handling each client. But it doesn't get you very far once you start to scale, and this is where the concurrency limits come in. And so what Igor did and how NGINX plays chess is that it takes a different approach. Yes, it has the pieces and the rules of the game, but it doesn't copy itself every time. It has one program, and it uses an event loop. So we lay out the pieces, we lay out the boards, and every time somebody comes up, they can pick a board that's not being used, and we'll see if they do something, and we'll move around. So it's like a grandmaster playing an exhibition game of chess in a room. If a move has been played, we'll respond. If a piece of data from the network comes in, we will handle it. If a response comes back from disk, we'll handle it. If there's any exceptions, we'll handle it, but we never wait, and that's the thing. So it allows us to have one process handling multiple concurrent games of chess, and we never wait for anything. When something happens, we take care of it. And this is really one of the primary innovations at NGINX brought that allowed this concurrency and the scaling to happen. The scaling, a lot of this comes from that asynchronous event loop, but also because we can pin each of these event loops to a CPU core. And so NGINX has multiple processes, each worker process can handle connections, and if we pin a process to a CPU core, then we basically begin near linear scaling. More CPUs, more throughput, more concurrency, because NGINX is not multi-threaded. It's multi-processed. So it's super lightweight. It scales really well, and that's what's led to a lot of NGINX's success as a web server and a reverse proxy. The async event loop means we keep nice and small. We can do more with less hardware resources. The multi-process architecture gives us near linear CPU scaling. So we scale up. You get nearly twice the number of connections if you double the CPUs, simple. And the other thing that it had was the ability to change the configuration, keep the old workers handling the existing connections, set up some new workers to handle the new configuration, and then clean it all up without any interruption. And this non-destructive configuration reload also made it very easy to run in production without any downtime. In fact, with NGINX, you can do a binary version upgrade without any downtime as well. And the result is that NGINX from similar timeline to our LAMP stack from the first open source release was in 2004. And then through to 2019 as a slow burn as the green line goes through the 1.0 release, a point T, and then overtaking Apache in sometime in 2019. Since then, it kind of thumbs up and down in a 30% bracket. So really popular, and actually that percentage number goes up if you look at the most popular sites, the high performance nature of NGINX. So it's been a super project. We're really proud of it. We have big investment to keep it there. We are looking to be releasing support for Quake and H2B3 later this year so that it continues to be the most popular web server on the planet. But that being laid out, I want to come back to the LAMP stack and look at some of the benefits and the challenges that it gave us when we were writing applications because it's the code that matters. Developers don't want to look after Linux platforms. They don't really want to look after databases. They want to write the code and they want to make it work. And it's the code that is the thing that creates the value. It's what creates new applications and new experiences and all the great stuff that we can build on the web. Everything else is in support of that in the web server as well. So kind of evolution and we abstract a few things. Typically in front of our web server or our first proxy, we've got something taking traffic off the Internet, a load balancer especially if we're scaling things away. Then the code and the framework is what's happening in the orange box. And the data store kind of got abstracted away by the frameworks themselves and just by the amount of choice that we have these days. So we don't have to use SQL. It's born to some other great databases, then no SQL databases, then object stores, cloud, non-cloud. It's just a data store. If you're a developer, usually you care less about architecting the data store. It's either chosen for you or the framework just makes persistent data available. And certainly below that, you really don't care. Just give me a run time, give me a kernel, make it run in a container on a VM, in the cloud, on a serverless platform, it's really less and less important. Little kernel is no less important, but the developers influence over it and how much they care about wanting to actually configure anything there is not so much. So developers care and control the code, their choice of framework, and typically the web layer that sits above that. Well, what's happened especially probably in the last five to eight years is that the layer above this has got really messy. So it used to be simple. You'd have the lamp stack on the web server, and then if you had the money to scale this out, you're in production, you need a load balancer because you need two things to happen. You need to get traffic from the internet to your application run time, and you need high availability for the service, the application you just deployed. The load balancer just has these two things to do. So as we scale out our application, we want to make sure the load balancer is sending traffic, A, to the right app, and B, to the right instance of the app that's got the least load or the most healthy, certainly not to anything that's temporarily down. So that's really simple, and then we had the Apache or the nginx part of that stack taking the connection for the load balancer. Then things start to get a little bit more interesting, especially as applications were no longer big bang deploy. So the more that we are updating things in real time, the least we want to wait and hang around for network teams running the load balancer to update stuff for us. So we sort of sort of shift to this pattern where we had load balancer at the edge, nice and static, takes care of the security stuff. They can do the WAF there, they can do TLS offloading, and then we'll do the traffic, direction of the traffic routing and take care of high availability concerns at a service proxy that knows how to get to all the different applications. This pattern's really common, it's really effective, and what we've seen in recent years is multiple teams tie themselves up in knots and stepping on each other's shoes to update the service proxy to launch a new service or change the way authentication happens on one of the applications. That kind of led us to this micro gateway model in API world or basically a DevOps proxy in another description where the team owns the application code and where it runs and the proxy that sits in front. And so the application behavior is defined by all of those things, so the team owns all the things. Security network team can keep running the load balancer, but it's doing less and less more and more is happening at the service proxy. And this is especially true in Kubernetes environments where we've got an ingress controller taking care of lots of stuff, the routing and high availability, and also increasingly things like authentication and rate limiting and now we have a questioner's like, who owns the configuration for the ingress controller? And the more developers that I speak to, it's not the development team. They want to do something different with routing, something different with authentication. They either have to ask another team, which goes back to the bad old days, or they have to build it into the application code, which is really brittle. And so we've got this service proxy set up which is getting more and more complex when moving more and more application behavior into the network layer, which is further and further away from the code. So it makes my head hurt. And what I want to talk about is that we should stop defining application behavior in multiple places. We should define it at the run time. It doesn't have to be at the code. Like you don't want to put TLS certificates in your code, you don't want to put authentication in your code. The microservices movement has been really good at saying, well, don't repeat yourself in code, separate your concerns away, don't do the most cutting things dealt with. And typically, that's what's been pushing stuff into the proxy. And then the proxy has half the application behavior that code as the other half, and for a developer to reproduce the production behavior on their dev environment becomes harder and harder. So, not completely seriously, but there's a team at Nginx whose mission is to kill this service proxy by building a better solution. So that the entire application behavior can be in a single piece of configuration at the run time, and without making the code all nasty. And that's how the Nginx unit project was born. I'll talk a lot more about that. It was born by the same founder, the same core team. There was some Nginx version 2 ideas back in 2013, 14, 15. And then there was an initiative inside Nginx to actually help run application code more natively. So with PHP and Python, there's another module that sits between the proxy and the application code that runs the processes that runs the application code. So we're trying to sort of simplify that. So we combined these ideas, some of the stuff we wanted to do with Nginx version 2 that was just going to be too hard, and some of this dynamic run time native application code running. And we started a new code base in 2016, so this project is not new. We did our 28th release yesterday. The first release was back in 2018. So, yeah, project's good for plus years old at this point. It's GA, and we call it the universal web app server. These t-shirts are available at our booth. So technically speaking, unit does three things to make this application development deployment easier. So it can serve static assets as a web server, of course. It can run application code without any other moving parts, and it can proxy to backends. So you've got everything that you need to build and deploy web application in one server component, one demon. And it doesn't matter what application program language you're using. The vision we have is that unit is the only server component you need to build web apps and APIs, and therefore you don't need another reverse proxy just to get traffic to it or to do special routing. It's not based on engine X. It doesn't have any code copied. It's a brand new code base, but it takes an involved architecture from that original project, and it's by decade and a half of learning about having the world's most popular apps. And so some design goals went into the project. Firstly, don't create a new configuration syntax, and don't continue to use the bespoke one we have with engine X. Reconfiguration should happen in situ, in process, not by cloning itself and then swapping out the live requests. And by decoupling network ports to your certificates and request routing from each other so they can all be independently controlled in a single place. And so engine X unit also plays chess. It has the same asynchronous event loop in terms of how we handle requests and how we process application requests. But this time it's multi-threaded because we want to be able to support the same multi-threaded capabilities that the application code might have. It also makes it a lot easier. This is what enables us to pass configuration changes direct to the thread without having to spawn new processes. So we've got a multi-processed and a multi-threaded architecture here. We've got the main process which creates and destroys the others, the controller process which supplies config to the router, and the router which is handling the application code. The requests and running the code. And we have processes that run the application code. So if we're running Python, PHP applications, whatever, unit is also directly controlling, managing, scaling up and down those application processes. And if you put a drawer, a box around this and call it a docker container, if you're deploying docker, you've got a single daemon with multiple applications. So you don't have to jump through hoops with a supervisor D or something else to make more complex entry points to make multiple daemons run. Yeah, one daemon and unit takes care of the rest. And these applications can be written in any of these languages. We support all of these frameworks and more. All of this is thoroughly documented on our website. And I'm talking about existing code. Not adapted for unit. Take what you have, run it in unit, and not have to do heavy lifting like the hardest things can be to do the routing logic in a web framework or to effectively serve static content from a framework. I'm not going to call names. Everyone's got pros and cons. Or handle TLS traffic. You don't want to be putting certificates in your code. So what unit allows us to do is decouple all those things and still support the native code as is. We can take Java apps and run them in containers without touching the code. Plenty of people doing that. So, let me show you with a little bit of hands on what it's about. One of the biggest changes that people notice when they come from engine X to unit is the configuration experience totally, totally different. So first of all, the configuration syntax that we've chosen is JSON. And the configuration experience is through an API. So you can have a file, but it's not a file-based config experience. Let me show you. So I'm going to just set up a simple web application from scratch here. Anyone that's good at spotting JSON typos, please be aware, ready to raise your hand and tell me that I missed a colon or something. We'll do it live. So first of all, okay, nothing running. So I'm going to start up the unit, demon, because I want to sort of see all the output. I'm going to keep it attached to the terminal here. And I'm going to send its messages to set that up. And I'm going to say I'm going to control it. The config API typically the unit socket for security just for convenience here. So I don't have to do so much typing. I'll make it listen on port 8080. So let's send that into the background. So unit starts as a demon. It's got a couple of language modules available to me. We have it on the slide. And now I've got the main process, the controller process and the router process. So let's hit this config API. So I'm going to use curl for pretty much everything. And I wanted it on port 8080. So if I hit the config endpoint, I get an empty JSON object. There's nothing happening. So let's create a file. No. Let's create a file. Good. And we're going to the main things that we unit cares about are listeners on ports, certificates, routes and applications. So we're going to start off with listener because nothing much happens without one of those. We can have as many as we like, but the nice thing is we can have one listener for multiple applications and let unit do the routing. So I'm going to listen on all my interfaces on port 9000 for this one. And what should unit do when I get a connection here is pass it to the routes or the router process. So there's a simple config. Cut that. Okay. I'm just going to pass it to jq to make sure I didn't do any, the syntax is good. And now I'll pass it back to units config API. So I'm going to use the put method to apply this configuration. I'm going to get the data from standard input through the pipeline. And it's going to go to port 8080 or slash config. Okay. No routes. We need a route. So let's do something. We'll create some routes to my routes object is an array. An array because the sequence is important. Otherwise, it could just be a list of objects. And my, it's an array of objects. And this is just going to be my default one. And I'm going to my action for this default route is to share some files on disk like web server. So let's see. They are in my demo directory. I've got an HTML file and I'll just pass the URI in there. That's a variable. So am I confident that just is going to work? Yeah, cool. So we've got, now we've got a listener report 9000. And so let me find a web browser. Yay. Okay. So I've got a hollow page and there's my HTML. There it is. And if I use a fancier HTTP client there's the response coming from my unit server. So we've got a super simple web server config that I've been sending with API calls, but that's not very exciting. It's the beginning. It's really just a hello world. What I want to show is unit running application code like natively. I've got very simple. Some might say a stupid web application. It's called add. It's a web API rest API that takes a JSON object as a post body. We read that from its input, get the size, and then we turn that request body into a JSON object inside Python. We initialize a response to 0 and then we iterate over every element of the operands array in that JSON body. And we cumulatively add those operands if you ever needed to add some numbers together. Here's some Python code. Then we'll deserialize that JSON at the end as part of our 200 response. This is as bad as simple as I can write WSGI Python web service. And usually you would have a GU Unicorn or a Unicorn, or use fast API and run, set that up. But I'm going to have unit run this code. So let's edit my conf again. I'm going to need to do two things. I'm going to need to add a root to know when I should run this code. And then I'm going to have to define the application itself. So let's do the root first. And of course I want this to be the first root because this one down here is my fallback. Put a comma in there. And this time I need to say on what match should this root apply. I can match on anything, right? URI, headers, the host, the cookies, query parameters, the HTTP method. They all have equal weight. I can compound them. I can use regular expressions. But of course I'm going to do the simplest possible thing. I just match on a explicit URI. Not a wildcard URI. Just we go to slash add. We're going to get the app. And then when I get that match, I'm going to define my action to be the application. So that is going to get passed to an application called add, which I now need to define. Otherwise we'll get another error. So let's create the applications object. And so note here a list of objects. I can have multiple applications. I don't constrain myself to just one Python app. I can do add in Python. I can do subtract in Ruby. And I can use Node.js for multiply. I can go crazy and create all those most ridiculous microservices web applications. And I have, but we're just going to do add right now. So let's create the add. That's what I called it. And I only need to tell it three things. Add a minimum. What type of application is it? Where is it? Copy paste this. And because it's Python what's the module name? So this is a language specific thing. It's called add because the file is add.py. No typos? Are we good? I'm not so confident. It's good. All right. So we wind back up in my history and apply the file I just edited through curl to units config interface. Reconfiguration is done. It's happy. And notice that we got these two log entries from appeared on standard output that we have the add application got started because we now know we have a Python app and a prototype which basically makes creating more add processes faster because I could also specify hey, give me 20 of these ready to handle requests scale up to 30 scale back down to five and all that kind of can be handled but we'll just let unit do its automatic thing. And let me look at my process list again. I've got main control on the root of process and now I've got my add prototype and application don't have G Unicorn, don't have another process manager. I only have everything was spawned from the unit main process here. And so if I were to create now my JSON body of operands and I do something exciting like adding one on one together and I can now pass this. Is it good? Now this was on port 9000 slash add. One on one is two. Let's even get dumps to standard output. You saw there was no error checking, right? So 3.44.555 is not actually a number. That's a number. And of course my welcome page is well, slow down. My hello world welcome page is still there. So I've got my web server, my content and I just have these processes. So unit can run my complete web application. I can start building it out from here. I can add TLS in the front. The thing that makes the difference is the developers control in control of TLS routing and the application code. And all of that runtime behavior is defined in my JSON config. And if I go ahead and look at what the unit has in the config entry point, that's everything that I've passed into it. Get it to be back in a nice readable format. And I can go I've been updating the entire thing at once, right? Just blowing it away and creating a new copy. That's non-destructive. There's no downtime involved there. But if I want to be very specific, I can look at the roots. If I've got an array, I can reference an element of that array by number and I can dig deeper. What's the match object? What's the URI for this? Oh, it's add. And let's say I want to change that. So I can say, well, let's make it sum instead of add. Send that to the same location. And I just reconfigured the root for this application on the fly. And if I run my if I go to add, it no longer works. If I go to sum, it does. So the configuration experience, the JSON, the very precise components, the atomic nature of the config changes are as big or as small as you like. Also means it's really super easy to automate, right? So your deployment pipelines can create applications, change roots, rotate certificates, whatever is needed. And so what we're trying to do is put that developer into control in a stack which has the code and has unit. And that's all you need to create web applications and have a great developer experience. And the other thing is it works the same on my laptop or if I bundle that into a container or if I send it to a VM. And everything I was doing there was straight on my machine here. So why use unit? Building applications, deploying applications get simpler. Microservices as you saw is super simple, you can also take Java monoliths or other large applications and turn them into cloud native applications. By decoupling the routing from the application code allows us to have a monolith build a microservice addition on the side in whatever language you want that to be in and control the routing in unit and do end-to-end TLS regardless of what the back end language is. The nice thing about TLS is the certificates get managed in the unit. The termination happens in unit and the context gets handed across shared memory. So there's no plain text across networks or sockets or PsyCars or anything like that. So it's super secure if you've got that kind of environment. We can also run these applications isolated. So we have support for Linux namespaces, C-groups, PID isolation, geruted file system so that you can run multiple apps, all those different mathematical operations can be completely isolated from one another and not break one another. So there's production grade concerns in terms of being a secure runtime too. And that's the content I have. Everything I mentioned and showed you is 100% open source. It's part of the Injects open source team. There's a dedicated team on this project. We have very active GitHub issues, contributions from the community. And we have community slacks as well for non-technical discussions and how-to discussions. The website, unitngx.org has extensive how-to docs for all the frameworks I showed on the slide. And it's super easy to get started from there or from our GitHub page. So that's everything I have and I've got a couple of minutes left if there's any questions. In five years' time, who would continue using Nginx when this sounds like the new black? In five years' time, that's a future we'd like to see. So for web applications, what Nginx is amazing at is being 100% reliable, rock solid, with an excellent security track record that if you need those things, there's no reason to change. Where we see pressure from the community is being able to change the configuration in real-time without having to duplicate itself and a configuration experience which is closer to an API than a file-based config. So Nginx isn't going to change any of those things. Its architecture is set and to change them would be extremely risky for a web server that runs on a large proportion of the internet. So we don't see unit overtaking or replacing Nginx at what it's really great at but we expect Nginx will be used closer to the edge, handling the dirty internet traffic as it comes off at the edge, with unit as the runtime. So fundamentally unit is a runtime platform for running web applications and web APIs. So there's definitely an overlap with what Nginx does there. But in a reference architecture view, you'd have Nginx out at the edge doing the security stuff and taking care of malformed requests and passing through the unit as a runtime. But the developer control is at unit. So we want to go back to having simpler, high speed layer 4 and SNI kind of low balancing. So it allows Nginx at the edge to go faster because it's not having routing, it's only doing the essential layer of authentication, maybe some rate limiting and everything else should be handled at the runtime between the code and what unit can do natively. Yeah, last one. So the API by default is on a unique socket. So we'd recommend not to expose it on network interface because right now, no, there's not. So extra authentication and security controls are coming. So I would now recommend you run it from a unique socket. But if it's in a typically in a Docker environment for example, you'd like do an idea and get it working and then you copy that and unit can bootstrap from a file. So when it starts up, it can read a state from a JSON file and then you can still have any mutable environment if that's the preference. Okay. It's quite a long list. So the simple answer is any HTTP metadata including cookies. Headers, query parameters, methods. So if it comes before the body, you can use it and we abstract things like query params and cookies into their component parts so they're easily available. All right. Thanks again.