More actions
No edit summary |
|||
| (19 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
This (unofficial) guide was made by [https://hackclub.slack.com/team/U094NTBR1S5 @twonum] (feel free to <abbr title="Dungeon Master</joke>Direct Message">DM</abbr> me for help). | This (unofficial) guide was made by [https://hackclub.slack.com/team/U094NTBR1S5 @twonum] (feel free to <abbr title="Dungeon Master</joke>Direct Message">DM</abbr> me for help). | ||
This guide works with all [https://docs.djangoproject.com/en/5.2/howto/deployment/ WSGI and ASGI servers] <em><strong>that is not</strong> Django's built-in [https://docs.djangoproject.com/en/5.2/ref/django-admin/#django-admin-runserver {{code|runserver}}] command</em>. | |||
; Tools this guide uses | ; Tools this guide uses | ||
* Hypercorn ASGI | * A [https://docs.djangoproject.com/en/5.2/howto/deployment/ WSGI and ASGI server] <em><strong>that is not</strong> Django's built-in [https://docs.djangoproject.com/en/5.2/ref/django-admin/#django-admin-runserver {{code|runserver}}] command</em> | ||
** I'll be using Hypercorn but this works with all WSGI/ASGI servers. | |||
* Caddy reverse proxy and web server with automatic HTTPS | * Caddy reverse proxy and web server with automatic HTTPS | ||
* PostgreSQL relational database for Django's ORM | * PostgreSQL relational database for Django's ORM | ||
| Line 77: | Line 76: | ||
pip install -r requirements.txt | pip install -r requirements.txt | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Also, install {{code|psycopg}} and your WSGI/ASGI server. | |||
== Set up Django settings == | == Set up Django settings == | ||
=== Use .env files with Django === | === Use .env files with Django === | ||
Use a .env file. I'll use django-environ:<syntaxhighlight lang="python3"> | Use a .env file. I'll use django-environ: | ||
<syntaxhighlight lang="python3"> | |||
from pathlib import Path | |||
import os | |||
from environ import Env | from environ import Env | ||
... | ... | ||
| Line 87: | Line 93: | ||
env = Env( | env = Env( | ||
DEBUG=(bool, False), | DEBUG=(bool, False), | ||
ALLOWED_HOSTS=(list, []), | |||
) | ) | ||
BASE_DIR = Path(__file__).resolve().parent.parent | |||
environ.Env.read_env(os.path.join(BASE_DIR, '.env')) | |||
def _env_file(key: str) -> str: | def _env_file(key: str) -> str: | ||
env_ = env(key) | env_ = env(key) | ||
| Line 98: | Line 108: | ||
DEBUG = env("DEBUG") | DEBUG = env("DEBUG") | ||
ALLOWED_HOSTS = env('ALLOWED_HOSTS') | |||
CSRF_TRUSTED_ORIGINS = [f"https://{h}" for h in ALLOWED_HOSTS] | |||
SECRET_KEY = env("SECRET_KEY") | SECRET_KEY = env("SECRET_KEY") | ||
DATABASES = { | DATABASES = { | ||
| Line 110: | Line 122: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== What your .env file should look like === | ==== What your .env file should look like ==== | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
DEBUG=False | DEBUG=False | ||
ALLOWED_HOSTS=$PROJECT_DOMAIN | |||
SECRET_KEY=please-change-this | SECRET_KEY=please-change-this | ||
DATABASE_URL=psql://$USERNAME:$NEST_PASSWORD@127.0.0.1:5432/$USERNAME_$PROJECT_NAME | DATABASE_URL=psql://$USERNAME:$NEST_PASSWORD@127.0.0.1:5432/$USERNAME_$PROJECT_NAME | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Some other options to tweak === | ==== Some other options to tweak ==== | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
STATIC_ROOT=$HOME/static/$PROJECT_NAME/static | STATIC_ROOT=$HOME/static/$PROJECT_NAME/static | ||
| Line 129: | Line 140: | ||
=== Migration and static collection === | === Migration and static collection === | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
source .env | |||
python3 manage.py migrate | python3 manage.py migrate | ||
python3 manage.py collectstatic | python3 manage.py collectstatic | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Set up | == Set up the WSGI/ASGI server == | ||
=== Get a port === | === Get a port === | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
nest get_port | nest get_port | ||
</syntaxhighlight>Remember this number and store it in the mental variable {{Code|$PORT}}. | </syntaxhighlight> | ||
Remember this number and store it in the mental variable {{Code|$PORT}}. | |||
From my experience, the port might get taken quickly. In that case, just get a new port and change the old one in existing files. | |||
This is the ''internal'' port (on Nest); the actual application uses port 443 (the standard HTTPS port). | |||
=== Create the run script === | === Create the run script === | ||
==== ~/$PROJECT_NAME.sh ==== | ==== ~/$PROJECT_NAME.sh ==== | ||
{{code|$PACKAGE_NAME}} is the name of the directory with settings.py. | |||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
#!/bin/bash | #!/bin/bash | ||
cd $PROJECT_NAME | cd ~/$PROJECT_NAME | ||
source .venv/bin/activate | source .venv/bin/activate | ||
hypercorn -b 127.0.0.1:$PORT $ | # WSGI/ASGI server command | ||
</syntaxhighlight> | |||
Add your WSGI or ASGI server's command. Bind it to {{code|127.0.0.1:$PORT}} with the application name of {{code|$PACKAGE_NAME.wsgi.application}} for WSGI and {{code|$PACKAGE_NAME.asgi:application}} for ASGI. See the [https://docs.djangoproject.com/en/5.2/howto/deployment/ Django deployment page] for info about servers. | |||
===== Example for Hypercorn ===== | |||
<syntaxhighlight lang="bash"> | |||
hypercorn -b 127.0.0.1:$PORT $PACKAGE_NAME.asgi:application | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 201: | Line 229: | ||
== Check it out == | == Check it out == | ||
Go to <code>$PROJECT_DOMAIN</code> in your browser. | Go to <code>$PROJECT_DOMAIN</code> in your browser. | ||
First load takes some time as Caddy needs to get a certificate from Let's Encrypt. | |||
It should probably work. If not, please Dungeon Master [https://hackclub.slack.com/team/U094NTBR1S5 @twonum] who is at fault for writing this guide. | It should probably work. If not, please Dungeon Master [https://hackclub.slack.com/team/U094NTBR1S5 @twonum] who is at fault for writing this guide. | ||
Latest revision as of 03:00, 25 July 2025
This (unofficial) guide was made by @twonum (feel free to DM me for help).
This guide works with all WSGI and ASGI servers that is not Django's built-in runserver command.
- Tools this guide uses
- A WSGI and ASGI server that is not Django's built-in
runservercommand- I'll be using Hypercorn but this works with all WSGI/ASGI servers.
- Caddy reverse proxy and web server with automatic HTTPS
- PostgreSQL relational database for Django's ORM
- systemd "daemon" (as it's called in Unix) to manage services
I'll assume that orpheus is your username and this is your directory structure:
- ~ (/home/orpheus)
- project
- .venv
- manage.py
- ...
- static
- project
- static
- media
- project
- project.sh
- Caddyfile
- .config
- ...
- project
I'll use $THIS_VARIABLE_NOTATION. Make a mental note of exports and replace the pseudo-variables with actual values.
Getting ready
First, get a shell to Nest. Don't have a Nest account?
ssh orpheus@hackclub.appGet Nest resources
export PROJECT_NAME="your project directory name" # set this to your project's directory name (like "project" in /home/$USERNAME/project)
nest db create $PROJECT_NAMEhackclub.app or custom domain?
$PROJECT_NAME.$USERNAME.hackclub.app (free)
export PROJECT_DOMAIN=$PROJECT_NAME.$USERNAME.hackclub.app
nest caddy add $PROJECT_DOMAINCustom domain (looks better, but you need your own DNS)
Add a CNAME record to $USERNAME.hackclub.app.
If you use Cloudflare or another proxy, turn the proxy off.
Wait and run this to check your DNS record:
export PROJECT_DOMAIN="hello.example.com" # set this
dig $PROJECT_DOMAIN CNAMEThen:
nest caddy add $PROJECT_DOMAINGet your project's files on Nest
Copy the directory containing manage.py to /home/$USERNAME/$PROJECT_NAME. See Quickstart#Using_the_Account.
cd ~/$PROJECT_NAMESet up venv and dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtAlso, install psycopg and your WSGI/ASGI server.
Set up Django settings
Use .env files with Django
Use a .env file. I'll use django-environ:
from pathlib import Path
import os
from environ import Env
...
env = Env(
DEBUG=(bool, False),
ALLOWED_HOSTS=(list, []),
)
BASE_DIR = Path(__file__).resolve().parent.parent
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
def _env_file(key: str) -> str:
env_ = env(key)
if env_[:2] == './':
return str(BASE_DIR / env_[2:])
return str(Path(env_))
...
DEBUG = env("DEBUG")
ALLOWED_HOSTS = env('ALLOWED_HOSTS')
CSRF_TRUSTED_ORIGINS = [f"https://{h}" for h in ALLOWED_HOSTS]
SECRET_KEY = env("SECRET_KEY")
DATABASES = {
'default': env.db(),
}
STATIC_URL = env("STATIC_URL")
STATIC_ROOT = _env_file("STATIC_ROOT")
MEDIA_URL = env("MEDIA_URL")
MEDIA_ROOT = _env_file("MEDIA_ROOT")
... # Use the same format for other optionsWhat your .env file should look like
DEBUG=False
ALLOWED_HOSTS=$PROJECT_DOMAIN
SECRET_KEY=please-change-this
DATABASE_URL=psql://$USERNAME:$NEST_PASSWORD@127.0.0.1:5432/$USERNAME_$PROJECT_NAMESome other options to tweak
STATIC_ROOT=$HOME/static/$PROJECT_NAME/static
STATIC_URL=/static/
MEDIA_ROOT=$HOME/static/$PROJECT_NAME/media
MEDIA_URL=/media/Migration and static collection
source .env
python3 manage.py migrate
python3 manage.py collectstaticSet up the WSGI/ASGI server
Get a port
nest get_portRemember this number and store it in the mental variable $PORT.
From my experience, the port might get taken quickly. In that case, just get a new port and change the old one in existing files.
This is the internal port (on Nest); the actual application uses port 443 (the standard HTTPS port).
Create the run script
~/$PROJECT_NAME.sh
$PACKAGE_NAME is the name of the directory with settings.py.
#!/bin/bash
cd ~/$PROJECT_NAME
source .venv/bin/activate
# WSGI/ASGI server commandAdd your WSGI or ASGI server's command. Bind it to 127.0.0.1:$PORT with the application name of $PACKAGE_NAME.wsgi.application for WSGI and $PACKAGE_NAME.asgi:application for ASGI. See the Django deployment page for info about servers.
Example for Hypercorn
hypercorn -b 127.0.0.1:$PORT $PACKAGE_NAME.asgi:applicationSet up systemd with the run script
~/.config/systemd/user/$PROJECT_NAME.service
[Unit]
Description=YOUR DESCRIPTION HERE
[Service]
WorkingDirectory=%h/$PROJECT_NAME
EnvironmentFile=%h/$PROJECT_NAME/.env
ExecStart=bash ../$PROJECT_NAME.sh
Restart=on-failure
[Install]
WantedBy=default.targetStart the service
systemctl --user daemon-reload
systemctl --user enable --now $PROJECT_NAME.service
systemctl --user status $PROJECT_NAME.serviceSet up the Caddy server
~/Caddyfile
Append this:
http://$PROJECT_DOMAIN {
bind unix/.$PROJECT_DOMAIN.webserver.sock|777
handle /static* {
root * $HOME/static/$PROJECT_NAME
file_server
}
handle /media* {
root * $HOME/static/$PROJECT_NAME
file_server
}
reverse_proxy :$PORT
}Restart Caddy service
systemctl --user restart caddy.serviceCheck it out
Go to $PROJECT_DOMAIN in your browser.
First load takes some time as Caddy needs to get a certificate from Let's Encrypt.
It should probably work. If not, please Dungeon Master @twonum who is at fault for writing this guide.
Avoiding hiccups
Make sure that it continues to work; if not, there's probably something wrong with the config.