Uvicorn with Sanic

If I decide to use Uvicorn with Sanic in production, how should I set it up?

From the Uvicorn docs: https://www.uvicorn.org/

For production deployments we recommend using gunicorn with the uvicorn worker class.

gunicorn example:app -w 4 -k uvicorn.workers.UvicornWorker

But sanic also provides its own worker class for gunicorn sanic.worker.GunicornWorker — so which worker am I supposed to use?

Also, and I don’t think that I have seen this before, but the Sanic docs said that: https://sanic.readthedocs.io/en/latest/sanic/deploying.html

Sanic’s own webserver is the fastest option, and it can be securely run on the Internet.

If that is the case, why should I use gunicorn / other asgi servers? I assume that there is an advantage with this with regards to worker restart and what not.

I started out with gunicorn and Sanic as well and I also had this thought; I couldn’t find any clear advantage so now I’m using supervisor to run sanic via python -m MyApp.
I’m interested to hear if you find any advantages.

I’ll try and post more later, and point you to some speed tests.

By far, the fastest option is the Sanic server running on its own.

next tier down would be gunicorn with uvicorn worker or uvicorn by itself. There is not a noticeable performance difference here.

Slowest option is gunicorn with Sanic worker for gunicorn. Why? it’s not async. It would essentially be just like running flask or any other wsgi server. You lose all the benefits of async Python. It is there as a convenience, but unless you know you need it, and depending upon the nature of the project, I would advise against this option.

So the question then becomes what features do you need? Gunicorn is very rich in this department. If you need this, then I would say the gunicorn with uvicorn worker is the best bet.

If you plan to run Sanic in a container, with a proxy (like nginx) in front, I would say skip gunicorn and just use the sanic server.

If you have specific ASGI Middleware you want to run, I think you would need to use hypercorn or uvicorn as a server. I have not tested this one though.

As a last note… latest version of sanic comes with a callable to run the server

sanic my.app --port=7777

I see. Didn’t know. I wish that I know about this months ago. Because the old docs imply that there is a reason for it and so I’ve used it.

Does the worker process restarts by itself if using the sanic module directly? I don’t have any further concrete reasons to use gunicorn / unicorn besides that it was the recommended deployment option in the old docs (which doesn’t appear to be there anymore).

Regarding using uvicorn by itself — if that is recommended, why does the uvicorn docs say to use gunicorn with uvicorn worker?

It does not. My message was based purely upon performance and not any other feature set. You obviously need to take into consideration what your deployment will be. For example, if you are using K8S, it can handle that part.

Ok, so with regards to this section of the docs:

Sanic has four: before startup, after startup, before shutdown, and after shutdown. Therefore, in ASGI mode, the startup and shutdown events will run consecutively and not actually around the server process beginning and ending (since that is now controlled by the ASGI server). Therefore, it is best to use after_server_start and before_server_stop.

Am I supposed to replace all of my before_server_start to after_server_start and after_server_stop to before_server_stop?

But if I do that, is initializing database before server start is still guaranteed?

You don’t have to replace them all. And if you did it doesn’t replace code that’s already built in. Listeners are to allow you to run extra code at those points in time.

1 Like

:point_up: What he said…

Ok, but here’s what I’ve noticed:

  • Everything works if I keep the code as is, but I get a ton of warnings when the app starts.
  • If I in fact change the code from before_server_start to after_server_start, things in fact don’t run because I do actually have some after_server_start code that depends on before_server_start has completed running.

Does after_server_start always happen after before_server_start? If so then I guess I don’t have to change anything? I could theoretically group them in such way that they are run in order, but they are spread out in different modules now and so it makes things slightly challenging.

Yes.

When in ASGI mode, there is no event outside of the server running. At least not for Sanic because the server is not Sanic. To compensate for this and still allow for apps to not break, those listeners are accepted and ran in the order expected. But technically that event has passed. hence the warning. Perhaps we should add a way to suppress it.

Separate, you can control the order of preference execution with the order in which you register the handlers. As I am on my phone right now, I don’t remember 100%, but I believe they are executed in reverse order.