How to Host a Django ASGI Server on a VPS

Hosting a Django ASGI server on a VPS can seem daunting due to the complexity of setting up production environments, configuring server tools, and managing security. Many developers struggle with ensuring the server handles requests properly, setting up async support, and managing the right dependencies.
But don’t worry — in this guide, we’ll walk you through the entire process step-by-step.
How to Securely Set Up SSH Access and User Management on a VPS Server
When setting up a VPS server (whether on DigitalOcean, AWS, or any other provider), it is essential to configure it securely to protect your server from unauthorized access. In this blog, we will walk through the steps to:
Set up SSH access to your VPS
Create a non-root user for managing your server
Grant the user sudo privileges
Secure the SSH configuration by disabling root login and password authentication
This process is crucial for ensuring that your server remains safe from brute-force attacks and that only authorized users can perform administrative tasks.
Step 1: SSH Access to Your VPS
The first step in securing your VPS is to ensure you can access it via SSH (Secure Shell) using a secure method. This will allow you to interact with the server remotely.
1.1 Connect to Your VPS
After creating your VPS instance (whether it's on DigitalOcean, AWS, or any VPS provider), you’ll typically be provided with an IP address and the ability to either use SSH keys or password-based authentication. In most cases, using SSH keys is the more secure option.
To SSH into your server using your private key:
ssh -i /path/to/your/private_key user@your-vps-ip
#example
ssh-key % ssh -i /Users/biseshadhikari/ssh-key root@167.71.225.134
Replace
/path/to/your/private_key
with the path to your private key file.Replace
user
with the correct username (commonlyroot
orubuntu
).Replace
your-vps-ip
with the actual IP address of your VPS.
Once logged in, you can proceed to set up a new user for better security.
Step 2: Create a New User (appuser
)
It is a best practice to avoid logging in as the root
user directly. Instead, you should create a new user with limited privileges, granting them only what is necessary.
2.1 Add a New User
Run the following command to create a new user called appuser
:
sudo adduser appuser
This will prompt you to enter a password and some additional user information (like Full Name, Room Number, etc.). You can press Enter to skip these optional fields.
2.2 Grant Sudo Privileges to appuser
By default, the appuser
will not have permission to run administrative commands. To grant them sudo privileges, you need to add them to the sudo
group.
To do this, run:
sudo usermod -aG sudo appuser
This command adds appuser
to the sudo
group, allowing them to execute commands with administrative privileges using sudo
.
2.3 Switch to the New User
To ensure the new user has proper sudo privileges, switch to the appuser
account:
su - appuser
Test that appuser
can use sudo
by running a simple command like sudo apt update
:
sudo apt update
You should be prompted to enter the password for appuser
, and the command should execute successfully.
Step 3: Disable Root Login and Password Authentication
Now that you have a non-root user with sudo privileges, it's time to enhance security by disabling root login and password-based authentication for SSH. This ensures that only users with valid SSH keys can access your server, reducing the risk of brute-force attacks.
3.1 Disable Root Login via SSH
To disable direct root login, you need to modify the SSH configuration file:
sudo nano /etc/ssh/sshd_config
Find the line that says:
#PermitRootLogin yes
Change it to:
PermitRootLogin no
This will disable root login over SSH, forcing users to log in using a non-root account.
3.2 Disable Password Authentication
Next, we will disable password-based authentication entirely and enforce SSH key authentication. In the same /etc/ssh/sshd_config
file, find the line:
#PasswordAuthentication yes
Change it to:
PasswordAuthentication no
This ensures that only SSH keys are allowed for authentication, adding an extra layer of security.
3.3 Restart SSH Service
After making the changes to the SSH configuration, restart the SSH service to apply the changes:
sudo systemctl restart sshd
Step 4: Verify the SSH Configuration
Now that we have made the necessary changes, let's verify everything works as expected.
4.1 Test Root Login
Try to log in to the server as root
:
ssh root@167.71.225.134
You should receive a message like:
Permission denied, please try again.
This confirms that root login has been successfully disabled.
4.2 Test Password Authentication
Try to log in using your username (appuser
) with the password:
ssh appuser@167.71.225.134
You should get a password prompt. If you try to enter the password, it will fail because we disabled password authentication. The only way to log in should be using the SSH key.
4.3 Test SSH Key Login
Now, log in as appuser
using your SSH key:
ssh -i /Users/biseshadhikari/ssh-key root@167.71.225.134
This should allow you to log in without requiring a password, confirming that key-based authentication is working properly.
Step 5: Clone the Django Channels Project and Set Up
At this stage, you have created a user (appuser
), configured SSH, and set up your server environment. Now, let's move forward with cloning your Django Channels project to your VPS and setting it up.
5.1 Clone the Project from GitHub
First, navigate to the /home/appuser/
directory where you want to store your project:
cd /home/appuser/
Clone the Django Channels project from the GitHub repository into your appuser
's home directory:
git clone https://github.com/yourusername/your-django-channels-project.git
- Replace
https://github.com/yourusername/your-django-channels-project.git
with the actual URL of your repository.
Once cloned, you’ll have the project stored in /home/appuser/your-django-channels-project
.
5.2 Set Up the Virtual Environment
To ensure that your Python environment is isolated and that dependencies are installed correctly, you need to create a virtual environment for the project.
- Create the virtual environment:
cd /home/appuser/channelproject
python3 -m venv venv
This creates a virtual environment called venv
in the project directory.
- Activate the virtual environment:
source venv/bin/activate
Once activated, your shell prompt should change to show that you’re working inside the virtual environment.
5.3 Install Dependencies
Now that the virtual environment is set up, you need to install the project dependencies. These dependencies are typically listed in the requirements.txt
file, which should be present in the root directory of the project.
Run the following command to install all the required packages:
pip install -r requirements.txt
This command will install Django, Channels, Redis, and any other dependencies specified in the requirements.txt
file.
5.4 Configure the Django Project Settings
Now you need to adjust the settings of the Django project to work correctly in a production environment.
- Open the settings file:
nano channelproject/project/settings.py
- Set Allowed Hosts:
In the settings file, find the ALLOWED_HOSTS
setting. This is where you specify which hostnames or IP addresses can access your Django project. You need to add your server’s IP address or domain name here. For example:
ALLOWED_HOSTS = ['167.71.225.233']
This will allow both the IP address and the domain name to access your Django Channels app.
- Set up Redis for Django Channels:
Since you're using Django Channels, you’ll need to configure Redis as the channel layer. In the settings.py
file, you need to add the following configuration:
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
Ensure that Redis is installed and running on the server
5.5 Create the Non-Login User (channeluser
) and Group (channelgroup
)
In Linux systems, it is a good practice to create dedicated users and groups for each project, especially for non-login users who don’t need to access the system interactively. This helps in improving the security and ensuring that each service or project has a dedicated user.
5.5.1 Create the User channeluser
The user channeluser
is created to own the project files and run tasks related to the Django Channels application, but it will not have login access to the server.
sudo useradd -r -M -d /home/appuser -s /bin/false channeluser
useradd
: Command to create a new user.-r
: Creates a system user (non-interactive), not intended for login purposes.-M
: Prevents the creation of a home directory for the user.-d /home/appuser
: Specifies the directory where this user’s files will be located (/home/appuser
).-s /bin/false
: Specifies that the user cannot log in to the system (shell access is denied).channeluser
: The name of the user being created.
5.5.2 Create the Group channelgroup
The channelgroup
group is used to manage access control for multiple users. This group will be responsible for managing access to the project folder.
sudo groupadd channelgroup
groupadd
: Command to create a new group.channelgroup
: The name of the group.
5.6 Add channeluser
to channelgroup
and Assign Permissions
After creating the user and group, the next step is to assign the user to the group. This allows the user channeluser
to inherit the permissions of the group channelgroup
.
5.6.1 Add channeluser
to channelgroup
sudo usermod -aG channelgroup channeluser
usermod
: Command to modify a user’s attributes.-aG
: This option appends the user to the group without removing the user from other groups.channelgroup
: The group that the user is being added to.channeluser
: The user being added to the group.
5.6.2 Add www-data
to channelgroup
The www-data
user is the default user for web servers like Nginx and Apache. By adding www-data
to channelgroup
, the web server can access the necessary files in the project directory.
sudo usermod -aG channelgroup www-data
www-data
: The default user for web servers.channelgroup
: The group thatwww-data
is being added to.
By adding www-data
to channelgroup
, you ensure that the web server (e.g., Nginx) has the required access to interact with the Django Channels project.
5.7: Set Ownership of Project Directory (/home/appuser/channelproject
)
Now that we have the user and group set up, we need to configure the permissions for the project directory. This will allow the user channeluser
to manage the files while allowing www-data
and other necessary users to access the files.
5.7.1 Set Ownership of the Project Directory
sudo chown -R channeluser:channelgroup /home/appuser/channelproject
chown -R
: Changes the ownership of files and directories recursively.channeluser:channelgroup
: Specifies thatchanneluser
should own the files andchannelgroup
is the group associated with the files./home/appuser/channelproject
: The directory that contains the Django Channels project.
This step ensures that channeluser
has control over the project files, while channelgroup
allows other users (like www-data
) to access them.
5.7.2 Set Permissions for the Project Directory
sudo chmod -R 750 /home/appuser/channelproject
chmod -R
: Modifies the permissions of files and directories recursively.750
: Sets the permissions in the following way:7 (Owner
channeluser
): Read, write, and execute permissions. This means thatchanneluser
has full control over the files.5 (Group
channelgroup
): Read and execute permissions. This allows members of the group (likewww-data
andappuser
) to read and execute files but not modify them.0 (Others): No permissions for others. This ensures that no unauthorized user can access the files.
These permissions make sure that:
Only
channeluser
can modify the files.Members of
channelgroup
(includingwww-data
) can read and execute the files, which is necessary for the web server to serve static files and interact with the Django Channels application.No other users can access the project files.
5.8: Set Permissions for /home/appuser
Directory
You also need to set the appropriate permissions for the /home/appuser
directory to ensure that channeluser
and channelgroup
members have access to it.
5.8.1 Set Ownership of /home/appuser
sudo chown -R channeluser:channelgroup /home/appuser
chown -R
: Changes the ownership of the/home/appuser
directory and all its contents recursively.channeluser:channelgroup
: This specifies thatchanneluser
is the owner of/home/appuser
and that the groupchannelgroup
is associated with it.
This ensures that the user channeluser
has control over the /home/appuser
directory, and members of channelgroup
have access to the files within the directory.
5.8.2 Set Permissions for /home/appuser
sudo chmod -R 750 /home/appuser
chmod -R
: Modifies the permissions of/home/appuser
and its contents recursively.750
: This allows:7 (Owner
channeluser
): Read, write, and execute permissions forchanneluser
to manage the directory and its contents.5 (Group
channelgroup
): Read and execute permissions for group members, allowing them to access the directory and files but not modify them.0 (Others): No permissions for other users to access the directory.
This ensures that only authorized users (those belonging to channelgroup
or channeluser
) can access the /home/appuser
directory and its contents, improving security.
Step 6: Set Up Redis and Daphne
Django Channels requires both Redis and Daphne to run properly.
6.1 Install Redis
To install Redis on your VPS, run the following commands:
sudo apt update
sudo apt install redis-server
Once Redis is installed, start it and enable it to start on boot:
sudo systemctl start redis
sudo systemctl enable redis
Redis is now running on your VPS and can be used by Django Channels for message brokering.
6.2 Install Daphne
Daphne is an ASGI server that runs your Django Channels application. If Daphne isn’t already listed in your project’s requirements.txt
, you can install it using pip:
pip install daphne
Once Daphne is installed, you can start the Daphne server to handle WebSocket connections.
6.3 Start the Daphne Server
You can start the Daphne server manually using the following command:
daphne -u /tmp/daphne.sock project.asgi:application
The
-u /tmp/daphne.sock
option creates a Unix socket that Nginx can use to communicate with Daphne.Replace
project.asgi:application
with the correct path to your ASGI application.
This command starts Daphne and connects it to your Django project.
Step 7: Configure Nginx for Reverse Proxy
Now that Daphne is running and accepting requests, you need to configure Nginx as a reverse proxy to forward HTTP and WebSocket traffic to Daphne.
7.1 Edit Nginx Configuration
Edit the Nginx configuration file to set up the reverse proxy:
sudo nano /etc/nginx/sites-available/default
Add the following configuration:
server {
listen 80;
server_name 167.71.225.233
# For regular HTTP requests
location / {
proxy_pass http://unix:/tmp/daphne.sock; # Forward traffic to Daphne via Unix socket
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# For WebSocket connections
location /ws/ {
proxy_pass http://unix:/tmp/daphne.sock; # Forward WebSocket traffic to Daphne via Unix socket
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket-specific headers
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
# Error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/html;
}
}
This configuration will forward all HTTP requests to the Daphne server via the Unix socket at /tmp/daphne.sock
.
7.2 Restart Nginx
After editing the Nginx configuration file, restart Nginx to apply the changes:
sudo systemctl restart nginx
Step 8: Set Up Daphne to Run on Boot
To ensure that Daphne starts automatically when your server reboots, you can create a systemd service for Daphne.
8.1 Create the Systemd Service
- Create a systemd service file for Daphne:
sudo nano /etc/systemd/system/daphne.service
- Add the following content:
[Unit]
Description=Daphne ASGI Server for Django Channels Project
After=network.target
[Service]
User=channeluser
Group=channelgroup
WorkingDirectory=/home/appuser/your-django-channels-project
ExecStart=/home/appuser/channelproject/venv/bin/daphne -u /tmp/daphne.sock project.asgi:application
Restart=always
[Install]
WantedBy=multi-user.target
- Enable and start the Daphne service:
sudo systemctl daemon-reload
sudo systemctl enable daphne
sudo systemctl start daphne
This will ensure Daphne runs automatically whenever the server is rebooted.
Step 9: Configure NGINX for the .sock
File
At this point, Daphne is running and listening on a Unix socket file (/tmp/daphne.sock
), but NGINX still needs to be configured to forward HTTP traffic to Daphne and ensure WebSockets work correctly. We will update the NGINX configuration to handle the proxying and proper routing.
9.1 NGINX Configuration
Create or Edit the NGINX Configuration File:
First, make sure you have a proper NGINX config file that forwards HTTP traffic to Daphne. If you don't already have the file, create it. If you do, you will modify it.
You should have an NGINX configuration file located in
/etc/nginx/sites-available/
for your project. If you haven’t created one, do so now. For example, if your project is calledchannels
, create the config file:nano /etc/nginx/sites-available/channels
Add NGINX Configuration:
Add the following configuration to the NGINX file. This configuration will forward requests to Daphne and handle the WebSocket connections on the specified paths.
server { listen 80; server_name 167.71.225.233 bisesh.xyz; # Replace with your server IP and domain name location / { proxy_pass http://unix:/tmp/daphne.sock; # Proxy HTTP traffic to Daphne using the Unix socket proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /ws/ { # WebSocket route for Django Channels proxy_pass http://unix:/tmp/daphne.sock; # Proxy WebSocket traffic to Daphne proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } error_page 500 502 503 504 /500.html; location = /500.html { root /var/www/html; } }
proxy_pass
http://unix:/tmp/daphne.sock
;
: This line ensures that all HTTP traffic (regular requests) is forwarded to Daphne via the Unix socket at/tmp/daphne.sock
.location /ws/
: This specific block handles WebSocket connections (which Django Channels uses). It forwards requests that are under the/ws/
path to Daphne while ensuring that the WebSocket upgrade headers are set properly.
Create a Symlink for the NGINX Site:
To link the configuration file to the NGINX server, you need to create a symbolic link to
/etc/nginx/sites-enabled/
from/etc/nginx/sites-available/
. This tells NGINX to use your configuration file.Run the following command to create the symlink:
sudo ln -s /etc/nginx/sites-available/channels /etc/nginx/sites-enabled/
Test NGINX Configuration:
Before restarting NGINX, it’s always a good idea to test the configuration to ensure there are no syntax errors.
Run the following command to test the configuration:
sudo nginx -t
If the output is successful and says
syntax is okay
andtest is successful
, then you can proceed. If there’s an error, check the configuration file for any issues.Reload NGINX:
After the configuration file has been tested, reload NGINX to apply the changes:
sudo systemctl reload nginx
This will make NGINX apply the new configuration and start forwarding traffic to Daphne via the Unix socket.
Step 10: Set Permissions for the .sock
File
Now that NGINX is configured to forward traffic to Daphne, we need to make sure that the Unix socket file (/tmp/daphne.sock
) is accessible by the necessary users (like channeluser
and channelgroup
, as well as nginx
which is often part of www-data
).
10.1 Set Permissions for the .sock
File
Daphne will create the socket file at /tmp/daphne.sock
, but by default, it might not have the correct permissions for NGINX to access it.
Change the Socket File’s Permissions:
You can set the correct permissions for the Unix socket by running:
sudo chmod 770 /tmp/daphne.sock sudo chown channeluser:channelgroup /tmp/daphne.sock
This will ensure that:
The
channeluser
(the user Daphne is running as) andchannelgroup
(the group associated withchanneluser
) have full access to the socket file.NGINX, which is running under the
www-data
group (on most servers), will have read and write permissions as well.
Ensure NGINX Can Access the Socket:
If your NGINX user (
www-data
) is not part of thechannelgroup
, add it to the group to allow proper access:sudo usermod -aG channelgroup www-data
This command adds the
www-data
user (which NGINX runs as) to thechannelgroup
. It ensures that NGINX can access the socket file.
Step 11: Test the Setup
Now that everything is set up, you can test the configuration:
Open your web browser and go to
http://167.71.225.233
orhttp://bisesh.xyz
(if you have set up the domain properly).The application should be up and running, with NGINX proxying requests to Daphne, and WebSockets working through
/ws/
.
If everything is working correctly, your application should be live and responsive, with NGINX forwarding both regular HTTP and WebSocket traffic to Daphne.
Conclusion
To summarize, we have:
Set up Daphne to run as a systemd service, ensuring it starts on boot.
Configured NGINX to forward both HTTP and WebSocket traffic to Daphne through a Unix socket.
Set the correct permissions on the Unix socket file, ensuring both NGINX and Daphne can access it.
Created the necessary symbolic link for NGINX and reloaded it to apply the changes.
This complete setup ensures that your Django Channels application runs efficiently with NGINX handling the reverse proxying and Daphne serving the WebSocket traffic.
Subscribe to my newsletter
Read articles from Bisesh Adhikari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Bisesh Adhikari
Bisesh Adhikari
Student | Tech enthusiast | Aspiring software developer